diff --git a/CHANGELOG.md b/CHANGELOG.md index ff408449e..08b8c93e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - Add support for Alpine. +- Add strong name signature to the OpenTelemetry.AutoInstrumentation assembly used + on the .NET Framework. + +### Changed ## [0.3.0-beta.1](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.3.0-beta.1) diff --git a/OpenTelemetry.AutoInstrumentation.sln b/OpenTelemetry.AutoInstrumentation.sln index f4c51f5b7..d340d378e 100644 --- a/OpenTelemetry.AutoInstrumentation.sln +++ b/OpenTelemetry.AutoInstrumentation.sln @@ -138,6 +138,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication.GrpcNetClie EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.AutoInstrumentation.AspNetCoreBootstrapper", "src\OpenTelemetry.AutoInstrumentation.AspNetCoreBootstrapper\OpenTelemetry.AutoInstrumentation.AspNetCoreBootstrapper.csproj", "{C1AEDAE0-6629-4C88-AB35-AB5B81FD50F6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication.StrongNamed", "test\test-applications\integrations\TestApplication.StrongNamed\TestApplication.StrongNamed.csproj", "{505A5B7B-930E-42D3-909F-E97C7C247FCF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -586,6 +588,18 @@ Global {C1AEDAE0-6629-4C88-AB35-AB5B81FD50F6}.Release|x64.Build.0 = Release|Any CPU {C1AEDAE0-6629-4C88-AB35-AB5B81FD50F6}.Release|x86.ActiveCfg = Release|Any CPU {C1AEDAE0-6629-4C88-AB35-AB5B81FD50F6}.Release|x86.Build.0 = Release|Any CPU + {505A5B7B-930E-42D3-909F-E97C7C247FCF}.Debug|Any CPU.ActiveCfg = Debug|x64 + {505A5B7B-930E-42D3-909F-E97C7C247FCF}.Debug|Any CPU.Build.0 = Debug|x64 + {505A5B7B-930E-42D3-909F-E97C7C247FCF}.Debug|x64.ActiveCfg = Debug|x64 + {505A5B7B-930E-42D3-909F-E97C7C247FCF}.Debug|x64.Build.0 = Debug|x64 + {505A5B7B-930E-42D3-909F-E97C7C247FCF}.Debug|x86.ActiveCfg = Debug|x86 + {505A5B7B-930E-42D3-909F-E97C7C247FCF}.Debug|x86.Build.0 = Debug|x86 + {505A5B7B-930E-42D3-909F-E97C7C247FCF}.Release|Any CPU.ActiveCfg = Release|x64 + {505A5B7B-930E-42D3-909F-E97C7C247FCF}.Release|Any CPU.Build.0 = Release|x64 + {505A5B7B-930E-42D3-909F-E97C7C247FCF}.Release|x64.ActiveCfg = Release|x64 + {505A5B7B-930E-42D3-909F-E97C7C247FCF}.Release|x64.Build.0 = Release|x64 + {505A5B7B-930E-42D3-909F-E97C7C247FCF}.Release|x86.ActiveCfg = Release|x86 + {505A5B7B-930E-42D3-909F-E97C7C247FCF}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -633,6 +647,7 @@ Global {671EB8F0-E164-4E9F-B423-27AF4B59D360} = {E409ADD3-9574-465C-AB09-4324D205CC7C} {0605872C-AB2B-4167-9B00-A525090D10BE} = {E409ADD3-9574-465C-AB09-4324D205CC7C} {C1AEDAE0-6629-4C88-AB35-AB5B81FD50F6} = {9E5F0022-0A50-40BF-AC6A-C3078585ECAB} + {505A5B7B-930E-42D3-909F-E97C7C247FCF} = {E409ADD3-9574-465C-AB09-4324D205CC7C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {160A1D00-1F5B-40F8-A155-621B4459D78F} diff --git a/src/OpenTelemetry.AutoInstrumentation.Native/calltarget_tokens.cpp b/src/OpenTelemetry.AutoInstrumentation.Native/calltarget_tokens.cpp index 542f3afd8..21ead8b29 100644 --- a/src/OpenTelemetry.AutoInstrumentation.Native/calltarget_tokens.cpp +++ b/src/OpenTelemetry.AutoInstrumentation.Native/calltarget_tokens.cpp @@ -4,6 +4,11 @@ #include "logger.h" #include "module_metadata.h" #include "otel_profiler_constants.h" +#include "cor_profiler.h" + +#ifndef _WIN32 +#include +#endif namespace trace { @@ -163,7 +168,9 @@ HRESULT CallTargetTokens::EnsureBaseCalltargetTokens() // *** Ensure profiler assembly ref if (profilerAssemblyRef == mdAssemblyRefNil) { - const AssemblyReference assemblyReference = *trace::AssemblyReference::GetFromCache(managed_profiler_full_assembly_version); + const auto bytecode_instrumentation_name = trace::profiler->GetBytecodeInstrumentationAssembly(); + Logger::Debug("CallTargetTokens::EnsureBaseCalltargetTokens() Bytecode Instrumentation Assembly: ", bytecode_instrumentation_name); + const AssemblyReference assemblyReference = *trace::AssemblyReference::GetFromCache(bytecode_instrumentation_name); ASSEMBLYMETADATA assembly_metadata{}; assembly_metadata.usMajorVersion = assemblyReference.version.major; diff --git a/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.cpp b/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.cpp index 3e27a970e..26eee858a 100644 --- a/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.cpp +++ b/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.cpp @@ -102,6 +102,13 @@ HRESULT STDMETHODCALLTYPE CorProfiler::Initialize(IUnknown* cor_profiler_info_un return E_FAIL; } + // code is ready to get runtime information + runtime_information_ = GetRuntimeInformation(this->info_); + if (process_name == WStr("w3wp.exe") || process_name == WStr("iisexpress.exe")) + { + is_desktop_iis = runtime_information_.is_desktop(); + } + // get ICorProfilerInfo6 for net46+ ICorProfilerInfo6* info6; hr = cor_profiler_info_unknown->QueryInterface(__uuidof(ICorProfilerInfo6), (void**)&info6); @@ -232,11 +239,6 @@ HRESULT STDMETHODCALLTYPE CorProfiler::Initialize(IUnknown* cor_profiler_info_un return E_FAIL; } - runtime_information_ = GetRuntimeInformation(this->info_); - if (process_name == WStr("w3wp.exe") || process_name == WStr("iisexpress.exe")) - { - is_desktop_iis = runtime_information_.is_desktop(); - } // writing opcodes vector for the IL dumper #define OPDEF(c, s, pop, push, args, type, l, s1, s2, flow) opcodes_names.push_back(s); @@ -262,10 +264,16 @@ HRESULT STDMETHODCALLTYPE CorProfiler::AssemblyLoadFinished(AssemblyID assembly_ { // if assembly failed to load, skip it entirely, // otherwise we can crash the process if module is not valid + Logger::Warn("AssemblyLoadFinished: ", assembly_id, " ", hr_status); CorProfilerBase::AssemblyLoadFinished(assembly_id, hr_status); return S_OK; } + if (Logger::IsDebugEnabled()) + { + Logger::Debug("AssemblyLoadFinished: ", assembly_id, " ", hr_status); + } + // keep this lock until we are done using the module, // to prevent it from unloading while in use std::lock_guard guard(module_id_to_info_map_lock_); @@ -279,89 +287,48 @@ HRESULT STDMETHODCALLTYPE CorProfiler::AssemblyLoadFinished(AssemblyID assembly_ const auto assembly_info = GetAssemblyInfo(this->info_, assembly_id); if (!assembly_info.IsValid()) { - Logger::Debug("AssemblyLoadFinished: ", assembly_id, " ", hr_status); return S_OK; } - const auto is_instrumentation_assembly = assembly_info.name == WStr("OpenTelemetry.AutoInstrumentation"); - - if (is_instrumentation_assembly) + const auto is_instrumentation_assembly = assembly_info.name == managed_profiler_name; + if (!is_instrumentation_assembly) { - if (Logger::IsDebugEnabled()) + return S_OK; + } + + if (Logger::IsDebugEnabled()) + { + Logger::Debug("AssemblyLoadFinished: Bytecode Instrumentation Assembly: ", GetBytecodeInstrumentationAssembly()); + } + + ComPtr metadata_interfaces; + auto hr = this->info_->GetModuleMetaData(assembly_info.manifest_module_id, ofRead | ofWrite, + IID_IMetaDataImport2, metadata_interfaces.GetAddressOf()); + if (FAILED(hr)) + { + Logger::Warn("AssemblyLoadFinished failed to get metadata interface for module id ", + assembly_info.manifest_module_id, " from assembly ", assembly_info.name); + return S_OK; + } + + // Get the IMetaDataAssemblyImport interface to get metadata from the managed assembly + const auto assembly_import = metadata_interfaces.As(IID_IMetaDataAssemblyImport); + const auto assembly_metadata = GetAssemblyImportMetadata(assembly_import); + + managed_profiler_loaded_app_domains.insert(assembly_info.app_domain_id); + + if (runtime_information_.is_desktop() && corlib_module_loaded) + { + // Set the managed_profiler_loaded_domain_neutral flag whenever the + // managed profiler is loaded shared + if (assembly_info.app_domain_id == corlib_app_domain_id) { - Logger::Debug("AssemblyLoadFinished: ", assembly_id, " ", hr_status); + Logger::Info("AssemblyLoadFinished: ", assembly_info.name, " was loaded domain-neutral"); + managed_profiler_loaded_domain_neutral = true; } - - ComPtr metadata_interfaces; - auto hr = this->info_->GetModuleMetaData(assembly_info.manifest_module_id, ofRead | ofWrite, - IID_IMetaDataImport2, metadata_interfaces.GetAddressOf()); - - if (FAILED(hr)) + else { - Logger::Warn("AssemblyLoadFinished failed to get metadata interface for module id ", - assembly_info.manifest_module_id, " from assembly ", assembly_info.name); - return S_OK; - } - - // Get the IMetaDataAssemblyImport interface to get metadata from the managed assembly - const auto assembly_import = metadata_interfaces.As(IID_IMetaDataAssemblyImport); - const auto assembly_metadata = GetAssemblyImportMetadata(assembly_import); - - // used multiple times for logging - const auto assembly_version = assembly_metadata.version.str(); - - if (Logger::IsDebugEnabled()) - { - Logger::Debug("AssemblyLoadFinished: AssemblyName=", assembly_info.name, - " AssemblyVersion=", assembly_version); - } - - if (is_instrumentation_assembly) - { - const auto expected_assembly_reference = trace::AssemblyReference(managed_profiler_full_assembly_version); - - // used multiple times for logging - const auto expected_version = expected_assembly_reference.version.str(); - - bool is_viable_version; - - if (runtime_information_.is_core()) - { - is_viable_version = (assembly_metadata.version >= expected_assembly_reference.version); - } - else - { - is_viable_version = (assembly_metadata.version == expected_assembly_reference.version); - } - - // Check that Major.Minor.Build matches the profiler version. - // On .NET Core, allow managed library to be a higher version than the native library. - if (is_viable_version) - { - Logger::Info("AssemblyLoadFinished: OpenTelemetry.AutoInstrumentation.dll v", assembly_version, - " matched profiler version v", expected_version); - managed_profiler_loaded_app_domains.insert(assembly_info.app_domain_id); - - if (runtime_information_.is_desktop() && corlib_module_loaded) - { - // Set the managed_profiler_loaded_domain_neutral flag whenever the - // managed profiler is loaded shared - if (assembly_info.app_domain_id == corlib_app_domain_id) - { - Logger::Info("AssemblyLoadFinished: OpenTelemetry.AutoInstrumentation.dll was loaded domain-neutral"); - managed_profiler_loaded_domain_neutral = true; - } - else - { - Logger::Info("AssemblyLoadFinished: OpenTelemetry.AutoInstrumentation.dll was not loaded domain-neutral"); - } - } - } - else - { - Logger::Warn("AssemblyLoadFinished: OpenTelemetry.AutoInstrumentation.dll v", assembly_version, - " did not match profiler version v", expected_version); - } + Logger::Info("AssemblyLoadFinished: ", assembly_info.name, " was not loaded domain-neutral"); } } @@ -928,6 +895,22 @@ bool CorProfiler::IsAttached() const return is_attached_; } +WSTRING CorProfiler::GetBytecodeInstrumentationAssembly() const +{ + WSTRING bytecodeInstrumentationAssembly = managed_profiler_full_assembly_version; + if (!runtime_information_.runtime_type) + { + Logger::Error("GetBytecodeInstrumentationAssembly: called before runtime_information was initialized."); + } + else if (!runtime_information_.is_core()) + { + // When on .NET Framework use the signature with the public key so strong name works. + bytecodeInstrumentationAssembly = managed_profiler_full_assembly_version_strong_name; + } + + return bytecodeInstrumentationAssembly; +} + // // Helper methods // @@ -985,15 +968,22 @@ bool CorProfiler::GetWrapperMethodRef(ModuleMetadata* module_metadata, ModuleID module_metadata->metadata_emit, module_metadata->assembly_import, module_metadata->assembly_emit); + const AssemblyReference* wrapper_assembly = &method_replacement.wrapper_method.assembly; + if (wrapper_assembly->name == managed_profiler_name) + { + // Handle the typical case in which the wrapper is also the bytecode instrumentation assembly. + wrapper_assembly = AssemblyReference::GetFromCache(GetBytecodeInstrumentationAssembly()); + } + // for each wrapper assembly, emit an assembly reference - hr = metadata_builder.EmitAssemblyRef(method_replacement.wrapper_method.assembly); + hr = metadata_builder.EmitAssemblyRef(*wrapper_assembly); if (FAILED(hr)) { Logger::Warn("JITCompilationStarted failed to emit wrapper assembly ref for assembly=", - method_replacement.wrapper_method.assembly.name, - ", Version=", method_replacement.wrapper_method.assembly.version.str(), - ", Culture=", method_replacement.wrapper_method.assembly.locale, - " PublicKeyToken=", method_replacement.wrapper_method.assembly.public_key.str()); + wrapper_assembly->name, + ", Version=", wrapper_assembly->version.str(), + ", Culture=", wrapper_assembly->locale, + " PublicKeyToken=", wrapper_assembly->public_key.str()); return false; } diff --git a/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.h b/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.h index b4b397983..2db4507b8 100644 --- a/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.h +++ b/src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.h @@ -88,6 +88,8 @@ public: bool IsAttached() const; + WSTRING GetBytecodeInstrumentationAssembly() const; + void GetAssemblyAndSymbolsBytes(BYTE** pAssemblyArray, int* assemblySize, BYTE** pSymbolsArray, int* symbolsSize) const; diff --git a/src/OpenTelemetry.AutoInstrumentation.Native/otel_profiler_constants.h b/src/OpenTelemetry.AutoInstrumentation.Native/otel_profiler_constants.h index 85a2b0fde..95985e9ab 100644 --- a/src/OpenTelemetry.AutoInstrumentation.Native/otel_profiler_constants.h +++ b/src/OpenTelemetry.AutoInstrumentation.Native/otel_profiler_constants.h @@ -76,10 +76,13 @@ const WSTRING mscorlib_assemblyName = WStr("mscorlib"); const WSTRING system_private_corelib_assemblyName = WStr("System.Private.CoreLib"); const WSTRING opentelemetry_autoinstrumentation_loader_assemblyName = WStr("OpenTelemetry.AutoInstrumentation.Loader"); +const WSTRING managed_profiler_name = WStr("OpenTelemetry.AutoInstrumentation"); + const WSTRING managed_profiler_full_assembly_version = WStr("OpenTelemetry.AutoInstrumentation, Version=0.3.0.0, Culture=neutral, PublicKeyToken=null"); -const WSTRING managed_profiler_name = WStr("OpenTelemetry.AutoInstrumentation"); +const WSTRING managed_profiler_full_assembly_version_strong_name = + WStr("OpenTelemetry.AutoInstrumentation, Version=0.3.0.0, Culture=neutral, PublicKeyToken=c0db600a13f60b51"); const WSTRING nonwindows_nativemethods_type = WStr("OpenTelemetry.AutoInstrumentation.NativeMethods+NonWindows"); diff --git a/src/OpenTelemetry.AutoInstrumentation/Instrumentations/StrongNamedValidation/StrongNamedValidation.cs b/src/OpenTelemetry.AutoInstrumentation/Instrumentations/StrongNamedValidation/StrongNamedValidation.cs new file mode 100644 index 000000000..9088a45ce --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Instrumentations/StrongNamedValidation/StrongNamedValidation.cs @@ -0,0 +1,57 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Diagnostics; +using OpenTelemetry.AutoInstrumentation.CallTarget; + +namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Validations +{ + /// + /// Instrumentation targeting the test application used to validate the strong name scenario. + /// When an actual bytecode instrumentation targeting a strong named assembly on .NET Framework + /// is added we can remove this instrumentation. + /// + [InstrumentMethod( + AssemblyName = "TestApplication.StrongNamed", + TypeName = "TestApplication.StrongNamed.Command", + MethodName = "Execute", + ReturnTypeName = ClrNames.Void, + ParameterTypeNames = new string[0], + MinimumVersion = "1.0.0", + MaximumVersion = "1.65535.65535", + IntegrationName = "StrongNamedValidation")] + public class StrongNamedValidation + { + private static readonly ActivitySource ValidationActivitySource = new ActivitySource("TestApplication.StrongNamedValidation"); + + /// + /// OnMethodBegin callback. + /// + /// Type of the target + /// Instance value, aka `this` of the instrumented method. + /// Calltarget state value + public static CallTargetState OnMethodBegin(TTarget instance) + { + using var activity = ValidationActivitySource.StartActivity(nameof(StrongNamedValidation)); + activity.AddTag("validation", nameof(StrongNamedValidation)); + + Console.WriteLine($"Validation: {nameof(StrongNamedValidation)}"); + + return CallTargetState.GetDefault(); + } + } +} diff --git a/src/OpenTelemetry.AutoInstrumentation/Properties/AssemblyInfo.NetFramework.cs b/src/OpenTelemetry.AutoInstrumentation/Properties/AssemblyInfo.NetFramework.cs new file mode 100644 index 000000000..136a96ebc --- /dev/null +++ b/src/OpenTelemetry.AutoInstrumentation/Properties/AssemblyInfo.NetFramework.cs @@ -0,0 +1,26 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETFRAMEWORK +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyKeyFileAttribute("keypair.snk")] + +[assembly: InternalsVisibleTo("OpenTelemetry.AutoInstrumentation.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001008db7c66f4ebdc6aac4196be5ce1ff4b59b020028e6dbd6e46f15aa40b3215975b92d0a8e45aba5f36114a8cb56241fbfa49f4c017e6c62197857e4e9f62451bc23d3a660e20861f95a57f23e20c77d413ad216ff1bb55f94104d4c501e32b03219d8603fb6fa73401c6ae6808c8daa61b9eaee5d2377d3c23c9ca6016c6582d8")] +[assembly: InternalsVisibleTo("IntegrationTests, PublicKey=00240000048000009400000006020000002400005253413100040000010001008db7c66f4ebdc6aac4196be5ce1ff4b59b020028e6dbd6e46f15aa40b3215975b92d0a8e45aba5f36114a8cb56241fbfa49f4c017e6c62197857e4e9f62451bc23d3a660e20861f95a57f23e20c77d413ad216ff1bb55f94104d4c501e32b03219d8603fb6fa73401c6ae6808c8daa61b9eaee5d2377d3c23c9ca6016c6582d8")] + +#endif diff --git a/src/OpenTelemetry.AutoInstrumentation/Properties/AssemblyInfo.cs b/src/OpenTelemetry.AutoInstrumentation/Properties/AssemblyInfo.cs index 203f11c1b..28b7bddf0 100644 --- a/src/OpenTelemetry.AutoInstrumentation/Properties/AssemblyInfo.cs +++ b/src/OpenTelemetry.AutoInstrumentation/Properties/AssemblyInfo.cs @@ -14,8 +14,10 @@ // limitations under the License. // +#if NETCOREAPP3_1_OR_GREATER using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("OpenTelemetry.AutoInstrumentation.AspNetCoreBootstrapper")] [assembly: InternalsVisibleTo("OpenTelemetry.AutoInstrumentation.Tests")] [assembly: InternalsVisibleTo("IntegrationTests")] +#endif diff --git a/src/OpenTelemetry.AutoInstrumentation/keypair.snk b/src/OpenTelemetry.AutoInstrumentation/keypair.snk new file mode 100644 index 000000000..2a982f599 Binary files /dev/null and b/src/OpenTelemetry.AutoInstrumentation/keypair.snk differ diff --git a/test/AssemblyInfo.cs b/test/AssemblyInfo.cs new file mode 100644 index 000000000..8b6f51f4d --- /dev/null +++ b/test/AssemblyInfo.cs @@ -0,0 +1,19 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Reflection; + +[assembly: AssemblyKeyFileAttribute("test-keypair.snk")] diff --git a/test/Directory.Build.props b/test/Directory.Build.props index a91ccdbe3..38e8f5fd8 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -30,4 +30,8 @@ + + + + diff --git a/test/IntegrationTests/Helpers/EnvironmentHelper.cs b/test/IntegrationTests/Helpers/EnvironmentHelper.cs index 39230ebbc..8f0752df5 100644 --- a/test/IntegrationTests/Helpers/EnvironmentHelper.cs +++ b/test/IntegrationTests/Helpers/EnvironmentHelper.cs @@ -207,9 +207,9 @@ public class EnvironmentHelper environmentVariables["OTEL_DOTNET_AUTO_INCLUDE_PROCESSES"] = Path.GetFileName(processToProfile); } - string integrations = GetIntegrationsPath(); environmentVariables["OTEL_DOTNET_AUTO_HOME"] = GetNukeBuildOutput(); - environmentVariables["OTEL_DOTNET_AUTO_INTEGRATIONS_FILE"] = integrations; + + environmentVariables["OTEL_DOTNET_AUTO_INTEGRATIONS_FILE"] = Environment.GetEnvironmentVariable("OTEL_DOTNET_AUTO_INTEGRATIONS_FILE") ?? GetIntegrationsPath(); if (testSettings.TracesSettings != null) { diff --git a/test/IntegrationTests/Helpers/TestHelper.cs b/test/IntegrationTests/Helpers/TestHelper.cs index 71e7acb27..76c8e7969 100644 --- a/test/IntegrationTests/Helpers/TestHelper.cs +++ b/test/IntegrationTests/Helpers/TestHelper.cs @@ -17,6 +17,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Reflection; using System.Threading.Tasks; using DotNet.Testcontainers.Builders; using DotNet.Testcontainers.Containers; @@ -47,6 +48,23 @@ public abstract class TestHelper protected ITestOutputHelper Output { get; } + /// + /// Gets the path for the test assembly, not the shadow copy created by xunit. + /// + public string GetTestAssemblyPath() + { +#if NETFRAMEWORK + // CodeBase is deprecated outside .NET Framework, instead of suppressing the error + // build the code as per recommendation for each runtime. + var codeBaseUrl = new Uri(Assembly.GetExecutingAssembly().CodeBase); + var codeBasePath = Uri.UnescapeDataString(codeBaseUrl.AbsolutePath); + var directory = Path.GetDirectoryName(codeBasePath); + return Path.GetFullPath(directory); +#else + return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); +#endif + } + public async Task StartContainerAsync(TestSettings testSettings, int webPort) { // get path to test application that the profiler will attach to diff --git a/test/IntegrationTests/IntegrationTests.csproj b/test/IntegrationTests/IntegrationTests.csproj index f6c4ed0f3..a10f3289f 100644 --- a/test/IntegrationTests/IntegrationTests.csproj +++ b/test/IntegrationTests/IntegrationTests.csproj @@ -1,11 +1,16 @@  - + + true + true + + + @@ -21,4 +26,10 @@ + + + + PreserveNewest + + diff --git a/test/IntegrationTests/StrongNamedTests.cs b/test/IntegrationTests/StrongNamedTests.cs new file mode 100644 index 000000000..21710123f --- /dev/null +++ b/test/IntegrationTests/StrongNamedTests.cs @@ -0,0 +1,59 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using FluentAssertions; +using FluentAssertions.Execution; +using IntegrationTests.Helpers; +using Xunit; +using Xunit.Abstractions; + +namespace IntegrationTests; + +public class StrongNamedTests : TestHelper +{ + public StrongNamedTests(ITestOutputHelper output) + : base("StrongNamed", output) + { + } + + [Fact] + public void SubmitsTraces() + { + var assemblyPath = GetTestAssemblyPath(); + var integrationsFile = Path.Combine(assemblyPath, "StrongNamedTestsIntegrations.json"); + File.Exists(integrationsFile).Should().BeTrue(); + + SetEnvironmentVariable("OTEL_DOTNET_AUTO_INTEGRATIONS_FILE", integrationsFile); + + using var agent = new MockZipkinCollector(Output); + + RunTestApplication(agent.Port); + + const int expectedSpansCount = 1; + var spans = agent.WaitForSpans(expectedSpansCount, TimeSpan.FromSeconds(5)); + + using (new AssertionScope()) + { + spans.Count.Should().Be(expectedSpansCount); + + spans.Count(s => s.Tags["validation"] == "StrongNamedValidation").Should().Be(1); + } + } +} diff --git a/test/IntegrationTests/StrongNamedTestsIntegrations.json b/test/IntegrationTests/StrongNamedTestsIntegrations.json new file mode 100644 index 000000000..5d945cbd4 --- /dev/null +++ b/test/IntegrationTests/StrongNamedTestsIntegrations.json @@ -0,0 +1,29 @@ +[ + { + "name": "StrongNamedTests", + "method_replacements": [ + { + "caller": {}, + "target": { + "assembly": "TestApplication.StrongNamed", + "type": "TestApplication.StrongNamed.Command", + "method": "Execute", + "signature_types": [ + "System.Void" + ], + "minimum_major": 1, + "minimum_minor": 0, + "minimum_patch": 0, + "maximum_major": 1, + "maximum_minor": 65535, + "maximum_patch": 65535 + }, + "wrapper": { + "assembly": "OpenTelemetry.AutoInstrumentation", + "type": "OpenTelemetry.AutoInstrumentation.Instrumentations.Validations.StrongNamedValidation", + "action": "CallTargetModification" + } + } + ] + } +] diff --git a/test/IntegrationTests/test-keypair.snk b/test/IntegrationTests/test-keypair.snk new file mode 100644 index 000000000..2a982f599 Binary files /dev/null and b/test/IntegrationTests/test-keypair.snk differ diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/OpenTelemetry.AutoInstrumentation.Tests.csproj b/test/OpenTelemetry.AutoInstrumentation.Tests/OpenTelemetry.AutoInstrumentation.Tests.csproj index 3efc88300..a5df10287 100644 --- a/test/OpenTelemetry.AutoInstrumentation.Tests/OpenTelemetry.AutoInstrumentation.Tests.csproj +++ b/test/OpenTelemetry.AutoInstrumentation.Tests/OpenTelemetry.AutoInstrumentation.Tests.csproj @@ -1,5 +1,9 @@  + + true + + diff --git a/test/OpenTelemetry.AutoInstrumentation.Tests/test-keypair.snk b/test/OpenTelemetry.AutoInstrumentation.Tests/test-keypair.snk new file mode 100644 index 000000000..2a982f599 Binary files /dev/null and b/test/OpenTelemetry.AutoInstrumentation.Tests/test-keypair.snk differ diff --git a/test/test-applications/integrations/TestApplication.StrongNamed/Command.cs b/test/test-applications/integrations/TestApplication.StrongNamed/Command.cs new file mode 100644 index 000000000..8b32128cc --- /dev/null +++ b/test/test-applications/integrations/TestApplication.StrongNamed/Command.cs @@ -0,0 +1,28 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Threading; + +namespace TestApplication.StrongNamed; + +public class Command +{ + public void Execute() + { + Thread.Yield(); // Just to have some call to outside code. + } +} diff --git a/test/test-applications/integrations/TestApplication.StrongNamed/Program.cs b/test/test-applications/integrations/TestApplication.StrongNamed/Program.cs new file mode 100644 index 000000000..c9eb90f45 --- /dev/null +++ b/test/test-applications/integrations/TestApplication.StrongNamed/Program.cs @@ -0,0 +1,31 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Reflection; + +[assembly: AssemblyKeyFileAttribute("keypair.snk")] + +namespace TestApplication.StrongNamed; + +public static class Program +{ + public static void Main(string[] args) + { + var command = new Command(); + command.Execute(); + } +} diff --git a/test/test-applications/integrations/TestApplication.StrongNamed/TestApplication.StrongNamed.csproj b/test/test-applications/integrations/TestApplication.StrongNamed/TestApplication.StrongNamed.csproj new file mode 100644 index 000000000..52e6553de --- /dev/null +++ b/test/test-applications/integrations/TestApplication.StrongNamed/TestApplication.StrongNamed.csproj @@ -0,0 +1,7 @@ + + + + Exe + + + diff --git a/test/test-applications/integrations/TestApplication.StrongNamed/keypair.snk b/test/test-applications/integrations/TestApplication.StrongNamed/keypair.snk new file mode 100644 index 000000000..f15bcd3b2 Binary files /dev/null and b/test/test-applications/integrations/TestApplication.StrongNamed/keypair.snk differ