From 4b3963ad34cd438c7b54e63b75e6c29ec13d21e9 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Wed, 13 Dec 2023 15:49:59 -0800 Subject: [PATCH 1/6] Add RuntimeStoreVersionRule --- .../RulesEngine/RuleEngine.cs | 3 +- .../RulesEngine/RuntimeStoreVersionRule.cs | 99 +++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs diff --git a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuleEngine.cs b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuleEngine.cs index 131d0e19bd..11fd60aeab 100644 --- a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuleEngine.cs +++ b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuleEngine.cs @@ -83,7 +83,8 @@ private static List CreateDefaultOptionalRules() { new OpenTelemetrySdkMinimumVersionRule(), new AssemblyFileVersionRule(), - new NativeProfilerDiagnosticsRule() + new NativeProfilerDiagnosticsRule(), + new RuntimeStoreVersionRule() }; } } diff --git a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs new file mode 100644 index 0000000000..0f9763139c --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs @@ -0,0 +1,99 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics; +using System.Reflection; +using OpenTelemetry.AutoInstrumentation.Logging; + +namespace OpenTelemetry.AutoInstrumentation.RulesEngine; + +internal class RuntimeStoreVersionRule : Rule +{ + private const string AdditionalDepsEnvironmentVariable = "DOTNET_ADDITIONAL_DEPS"; + private static readonly IOtelLogger Logger = OtelLogging.GetLogger("StartupHook"); + + public RuntimeStoreVersionRule() + { + Name = "Runtime Store Assembly Validator"; + Description = "Ensure that the runtime store assembly versions are not lower than the version used by the Application"; + } + + internal override bool Evaluate() + { + var result = true; + + try + { + if (Environment.GetEnvironmentVariable(AdditionalDepsEnvironmentVariable) == null) + { + Logger.Warning($"Rule Engine: {AdditionalDepsEnvironmentVariable} environment variable not found. Skipping rule evaluation."); + return result; + } + + var configuredStoreDirectory = GetConfiguredStoreDirectory(); + if (configuredStoreDirectory == null) + { + // Store location not found, skip rule evaluation + return result; + } + + string[] storeFiles = Directory.GetFiles(configuredStoreDirectory, "*.dll", SearchOption.AllDirectories); + + foreach (string file in storeFiles) + { + var runTimeStoreFileVersionInfo = FileVersionInfo.GetVersionInfo(file); + var runTimeStoreFileVersion = new Version(runTimeStoreFileVersionInfo.FileVersion); + + var assemblyName = Path.GetFileNameWithoutExtension(file); + var appInstrumentationAssembly = Assembly.Load(assemblyName); + var appInstrumentationFileVersionInfo = FileVersionInfo.GetVersionInfo(appInstrumentationAssembly.Location); + var appInstrumentationFileVersion = new Version(appInstrumentationFileVersionInfo.FileVersion); + + if (appInstrumentationFileVersion < runTimeStoreFileVersion) + { + result = false; + Logger.Error($"Rule Engine: Application has direct or indirect reference to lower version of runtime store assembly {runTimeStoreFileVersionInfo.FileName} - {appInstrumentationFileVersion}."); + } + else + { + Logger.Information($"Rule Engine: Runtime store assembly {runTimeStoreFileVersionInfo.FileName} is validated successfully."); + } + } + } + catch (Exception) + { + // Exception in rule evaluation should not impact the result of the rule. + Logger.Warning("Rule Engine: Couldn't evaluate reference to runtime store assemblies in an app."); + throw; + } + + return result; + } + + private static string? GetConfiguredStoreDirectory() + { + try + { + var tracerHomeDirectory = Directory.GetParent(StartupHook.LoaderAssemblyLocation).ToString().ToString(); + var storeDirectory = Path.Combine(tracerHomeDirectory, "store"); + + // Check if the store directory exists + if (!Directory.Exists(storeDirectory)) + { + Logger.Warning($"Rule Engine: Runtime store directory not found at {storeDirectory}. Skipping rule evaluation."); + return null; + } + + var architecture = Environment.Is64BitProcess ? "x64" : "x86"; + string targetFramework = "net" + Environment.Version.Major.ToString() + "." + Environment.Version.Minor.ToString(); + var finalPath = Path.Combine(storeDirectory, architecture.ToString(), targetFramework); + + return finalPath; + } + catch (Exception ex) + { + Logger.Error($"Error getting store directory location: {ex}"); + throw; + } + } +} From 5aa95de76b917e8d64383b0cddfdc5a35a5197c6 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Wed, 13 Dec 2023 16:40:24 -0800 Subject: [PATCH 2/6] Switch to environment variable. --- .../RulesEngine/RuntimeStoreVersionRule.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs index 0f9763139c..c8219b69c4 100644 --- a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs +++ b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs @@ -9,7 +9,7 @@ namespace OpenTelemetry.AutoInstrumentation.RulesEngine; internal class RuntimeStoreVersionRule : Rule { - private const string AdditionalDepsEnvironmentVariable = "DOTNET_ADDITIONAL_DEPS"; + private const string RuntimeStoreEnvironmentVariable = "DOTNET_SHARED_STORE"; private static readonly IOtelLogger Logger = OtelLogging.GetLogger("StartupHook"); public RuntimeStoreVersionRule() @@ -24,12 +24,6 @@ internal override bool Evaluate() try { - if (Environment.GetEnvironmentVariable(AdditionalDepsEnvironmentVariable) == null) - { - Logger.Warning($"Rule Engine: {AdditionalDepsEnvironmentVariable} environment variable not found. Skipping rule evaluation."); - return result; - } - var configuredStoreDirectory = GetConfiguredStoreDirectory(); if (configuredStoreDirectory == null) { @@ -37,9 +31,9 @@ internal override bool Evaluate() return result; } - string[] storeFiles = Directory.GetFiles(configuredStoreDirectory, "*.dll", SearchOption.AllDirectories); + var storeFiles = Directory.GetFiles(configuredStoreDirectory, "*.dll", SearchOption.AllDirectories); - foreach (string file in storeFiles) + foreach (var file in storeFiles) { var runTimeStoreFileVersionInfo = FileVersionInfo.GetVersionInfo(file); var runTimeStoreFileVersion = new Version(runTimeStoreFileVersionInfo.FileVersion); @@ -74,8 +68,12 @@ internal override bool Evaluate() { try { - var tracerHomeDirectory = Directory.GetParent(StartupHook.LoaderAssemblyLocation).ToString().ToString(); - var storeDirectory = Path.Combine(tracerHomeDirectory, "store"); + var storeDirectory = Environment.GetEnvironmentVariable(RuntimeStoreEnvironmentVariable); + if (storeDirectory == null) + { + Logger.Warning($"Rule Engine: {RuntimeStoreEnvironmentVariable} environment variable not found. Skipping rule evaluation."); + return null; + } // Check if the store directory exists if (!Directory.Exists(storeDirectory)) @@ -85,7 +83,7 @@ internal override bool Evaluate() } var architecture = Environment.Is64BitProcess ? "x64" : "x86"; - string targetFramework = "net" + Environment.Version.Major.ToString() + "." + Environment.Version.Minor.ToString(); + var targetFramework = "net" + Environment.Version.Major.ToString() + "." + Environment.Version.Minor.ToString(); var finalPath = Path.Combine(storeDirectory, architecture.ToString(), targetFramework); return finalPath; From c913eacfcc32ef82564b609dbc0ded09bd08984a Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Fri, 15 Dec 2023 15:14:17 -0800 Subject: [PATCH 3/6] Add special case. --- .../RulesEngine/RuntimeStoreVersionRule.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs index c8219b69c4..800971f3c9 100644 --- a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs +++ b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs @@ -31,18 +31,26 @@ internal override bool Evaluate() return result; } - var storeFiles = Directory.GetFiles(configuredStoreDirectory, "*.dll", SearchOption.AllDirectories); + var storeFiles = Directory.GetFiles(configuredStoreDirectory, "Microsoft.Extensions*.dll", SearchOption.AllDirectories); foreach (var file in storeFiles) { - var runTimeStoreFileVersionInfo = FileVersionInfo.GetVersionInfo(file); - var runTimeStoreFileVersion = new Version(runTimeStoreFileVersionInfo.FileVersion); - var assemblyName = Path.GetFileNameWithoutExtension(file); var appInstrumentationAssembly = Assembly.Load(assemblyName); var appInstrumentationFileVersionInfo = FileVersionInfo.GetVersionInfo(appInstrumentationAssembly.Location); var appInstrumentationFileVersion = new Version(appInstrumentationFileVersionInfo.FileVersion); + if (appInstrumentationFileVersion.Major < 5) + { + // Special case to handle runtime store version 3.1.x.x package references in app. + // Skip rule evaluation for assemblies with version 3.1.x.x. + Logger.Debug($"Rule Engine: Skipping rule evaluation for runtime store assembly {appInstrumentationFileVersionInfo.FileName} with version {appInstrumentationFileVersion}."); + continue; + } + + var runTimeStoreFileVersionInfo = FileVersionInfo.GetVersionInfo(file); + var runTimeStoreFileVersion = new Version(runTimeStoreFileVersionInfo.FileVersion); + if (appInstrumentationFileVersion < runTimeStoreFileVersion) { result = false; @@ -50,7 +58,7 @@ internal override bool Evaluate() } else { - Logger.Information($"Rule Engine: Runtime store assembly {runTimeStoreFileVersionInfo.FileName} is validated successfully."); + Logger.Debug($"Rule Engine: Runtime store assembly {runTimeStoreFileVersionInfo.FileName} is validated successfully."); } } } From 5a22cbf50e8a11bbb07f72d4743435c17c36ad80 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Fri, 15 Dec 2023 15:28:58 -0800 Subject: [PATCH 4/6] Add exception handler for AssemblyLoad to get full pass. --- .../RulesEngine/RuntimeStoreVersionRule.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs index 800971f3c9..4b9ab2909d 100644 --- a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs +++ b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs @@ -36,7 +36,17 @@ internal override bool Evaluate() foreach (var file in storeFiles) { var assemblyName = Path.GetFileNameWithoutExtension(file); - var appInstrumentationAssembly = Assembly.Load(assemblyName); + Assembly appInstrumentationAssembly; + try + { + appInstrumentationAssembly = Assembly.Load(assemblyName); + } + catch (Exception ex) + { + Logger.Warning(ex, $"Rule Engine: Assembly load failed. Skipping rule evaluation for assembly - {assemblyName}"); + continue; + } + var appInstrumentationFileVersionInfo = FileVersionInfo.GetVersionInfo(appInstrumentationAssembly.Location); var appInstrumentationFileVersion = new Version(appInstrumentationFileVersionInfo.FileVersion); @@ -62,10 +72,10 @@ internal override bool Evaluate() } } } - catch (Exception) + catch (Exception ex) { // Exception in rule evaluation should not impact the result of the rule. - Logger.Warning("Rule Engine: Couldn't evaluate reference to runtime store assemblies in an app."); + Logger.Warning(ex, "Rule Engine: Couldn't evaluate reference to runtime store assemblies in an app."); throw; } @@ -92,13 +102,13 @@ internal override bool Evaluate() var architecture = Environment.Is64BitProcess ? "x64" : "x86"; var targetFramework = "net" + Environment.Version.Major.ToString() + "." + Environment.Version.Minor.ToString(); - var finalPath = Path.Combine(storeDirectory, architecture.ToString(), targetFramework); + var finalPath = Path.Combine(storeDirectory, architecture, targetFramework); return finalPath; } catch (Exception ex) { - Logger.Error($"Error getting store directory location: {ex}"); + Logger.Warning(ex, "Error getting store directory location"); throw; } } From cf709809281bd17025281ade9bb1949b316feee6 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Fri, 5 Jan 2024 12:31:30 -0800 Subject: [PATCH 5/6] Change to diagnostic rule --- .../RulesEngine/RuleEngine.cs | 4 +- ...nRule.cs => RuntimeStoreDiagnosticRule.cs} | 50 ++++++++++++++----- 2 files changed, 39 insertions(+), 15 deletions(-) rename src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/{RuntimeStoreVersionRule.cs => RuntimeStoreDiagnosticRule.cs} (66%) diff --git a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuleEngine.cs b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuleEngine.cs index 11fd60aeab..2cd321fe6e 100644 --- a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuleEngine.cs +++ b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuleEngine.cs @@ -81,10 +81,10 @@ private static List CreateDefaultOptionalRules() { return new() { + new RuntimeStoreDiagnosticRule(), new OpenTelemetrySdkMinimumVersionRule(), new AssemblyFileVersionRule(), - new NativeProfilerDiagnosticsRule(), - new RuntimeStoreVersionRule() + new NativeProfilerDiagnosticsRule() }; } } diff --git a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreDiagnosticRule.cs similarity index 66% rename from src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs rename to src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreDiagnosticRule.cs index 4b9ab2909d..ae0fc6140f 100644 --- a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreVersionRule.cs +++ b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreDiagnosticRule.cs @@ -7,28 +7,34 @@ namespace OpenTelemetry.AutoInstrumentation.RulesEngine; -internal class RuntimeStoreVersionRule : Rule +internal class RuntimeStoreDiagnosticRule : Rule { private const string RuntimeStoreEnvironmentVariable = "DOTNET_SHARED_STORE"; private static readonly IOtelLogger Logger = OtelLogging.GetLogger("StartupHook"); - public RuntimeStoreVersionRule() + public RuntimeStoreDiagnosticRule() { - Name = "Runtime Store Assembly Validator"; - Description = "Ensure that the runtime store assembly versions are not lower than the version used by the Application"; + Name = "Runtime Store Diagnostic Rule"; + Description = "Logs detail that assembly versions in the runtime store are not lower than the version the Application uses."; } internal override bool Evaluate() { - var result = true; - try { + // Skip rule evaluation if the application is running in self-contained mode. + if (IsSelfContained()) + { + Logger.Debug("Rule Engine: Skipping rule evaluation for self-contained application."); + return true; + } + var configuredStoreDirectory = GetConfiguredStoreDirectory(); if (configuredStoreDirectory == null) { // Store location not found, skip rule evaluation - return result; + Logger.Debug("Rule Engine: Skipping rule evaluation as runtime store location is not found."); + return true; } var storeFiles = Directory.GetFiles(configuredStoreDirectory, "Microsoft.Extensions*.dll", SearchOption.AllDirectories); @@ -37,6 +43,7 @@ internal override bool Evaluate() { var assemblyName = Path.GetFileNameWithoutExtension(file); Assembly appInstrumentationAssembly; + try { appInstrumentationAssembly = Assembly.Load(assemblyName); @@ -63,8 +70,7 @@ internal override bool Evaluate() if (appInstrumentationFileVersion < runTimeStoreFileVersion) { - result = false; - Logger.Error($"Rule Engine: Application has direct or indirect reference to lower version of runtime store assembly {runTimeStoreFileVersionInfo.FileName} - {appInstrumentationFileVersion}."); + Logger.Warning($"Rule Engine: Application has direct or indirect reference to lower version of runtime store assembly {runTimeStoreFileVersionInfo.FileName} - {appInstrumentationFileVersion}. "); } else { @@ -76,10 +82,10 @@ internal override bool Evaluate() { // Exception in rule evaluation should not impact the result of the rule. Logger.Warning(ex, "Rule Engine: Couldn't evaluate reference to runtime store assemblies in an app."); - throw; } - return result; + // This a diagnostic rule, so we always return true. + return true; } private static string? GetConfiguredStoreDirectory() @@ -87,16 +93,17 @@ internal override bool Evaluate() try { var storeDirectory = Environment.GetEnvironmentVariable(RuntimeStoreEnvironmentVariable); + // Skip rule evaluation if the store directory is not configured. if (storeDirectory == null) { - Logger.Warning($"Rule Engine: {RuntimeStoreEnvironmentVariable} environment variable not found. Skipping rule evaluation."); + Logger.Debug($"Rule Engine: {RuntimeStoreEnvironmentVariable} environment variable not found. Skipping rule evaluation."); return null; } // Check if the store directory exists if (!Directory.Exists(storeDirectory)) { - Logger.Warning($"Rule Engine: Runtime store directory not found at {storeDirectory}. Skipping rule evaluation."); + Logger.Debug($"Rule Engine: Runtime store directory not found at {storeDirectory}. Skipping rule evaluation."); return null; } @@ -112,4 +119,21 @@ internal override bool Evaluate() throw; } } + + private static bool IsSelfContained() + { + var assemblyPath = Assembly.GetExecutingAssembly().Location; + var directory = Path.GetDirectoryName(assemblyPath); + + // Check for the presence of a known .NET runtime file + if (directory != null && + (File.Exists(Path.Combine(directory, "coreclr.dll")) || + File.Exists(Path.Combine(directory, "libcoreclr.so")) || + File.Exists(Path.Combine(directory, "libcoreclr.dylib")))) + { + return true; + } + + return false; + } } From cff6f98e10eed091a668d717b3b0ad01deac8338 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Thu, 11 Jan 2024 09:44:26 -0800 Subject: [PATCH 6/6] Change targetFramework to use interpolated string. --- .../RulesEngine/RuntimeStoreDiagnosticRule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreDiagnosticRule.cs b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreDiagnosticRule.cs index ae0fc6140f..4e60d0505d 100644 --- a/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreDiagnosticRule.cs +++ b/src/OpenTelemetry.AutoInstrumentation.StartupHook/RulesEngine/RuntimeStoreDiagnosticRule.cs @@ -108,7 +108,7 @@ internal override bool Evaluate() } var architecture = Environment.Is64BitProcess ? "x64" : "x86"; - var targetFramework = "net" + Environment.Version.Major.ToString() + "." + Environment.Version.Minor.ToString(); + var targetFramework = $"net{Environment.Version.Major}.{Environment.Version.Minor}"; var finalPath = Path.Combine(storeDirectory, architecture, targetFramework); return finalPath;