Skip to content

Commit bb56733

Browse files
authored
Restore assembly redirection for .NET Framework (open-telemetry#2479)
1 parent 07cc775 commit bb56733

File tree

5 files changed

+135
-13
lines changed

5 files changed

+135
-13
lines changed

Diff for: OpenTelemetry.AutoInstrumentation.sln

+16-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication.SelfContain
184184
EndProject
185185
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication.EntityFrameworkCore.Pomelo.MySql", "test\test-applications\integrations\TestApplication.EntityFrameworkCore.Pomelo.MySql\TestApplication.EntityFrameworkCore.Pomelo.MySql.csproj", "{1D7E11AA-27B6-4863-B5EC-1F0ECC6979B2}"
186186
EndProject
187-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTelemetry.AutoInstrumentation.StartupHook.Tests", "test\OpenTelemetry.AutoInstrumentation.StartupHook.Tests\OpenTelemetry.AutoInstrumentation.StartupHook.Tests.csproj", "{0077F121-DC2B-425E-A2F4-DEAC2A566E14}"
187+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.AutoInstrumentation.StartupHook.Tests", "test\OpenTelemetry.AutoInstrumentation.StartupHook.Tests\OpenTelemetry.AutoInstrumentation.StartupHook.Tests.csproj", "{0077F121-DC2B-425E-A2F4-DEAC2A566E14}"
188+
EndProject
189+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication.AssemblyRedirection.NetFramework", "test\test-applications\integrations\TestApplication.AssemblyRedirection.NetFramework\TestApplication.AssemblyRedirection.NetFramework.csproj", "{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}"
188190
EndProject
189191
Global
190192
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -862,6 +864,18 @@ Global
862864
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Release|x64.Build.0 = Release|Any CPU
863865
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Release|x86.ActiveCfg = Release|Any CPU
864866
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Release|x86.Build.0 = Release|Any CPU
867+
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Debug|Any CPU.ActiveCfg = Debug|x64
868+
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Debug|Any CPU.Build.0 = Debug|x64
869+
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Debug|x64.ActiveCfg = Debug|x64
870+
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Debug|x64.Build.0 = Debug|x64
871+
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Debug|x86.ActiveCfg = Debug|x86
872+
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Debug|x86.Build.0 = Debug|x86
873+
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Release|Any CPU.ActiveCfg = Release|x64
874+
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Release|Any CPU.Build.0 = Release|x64
875+
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Release|x64.ActiveCfg = Release|x64
876+
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Release|x64.Build.0 = Release|x64
877+
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Release|x86.ActiveCfg = Release|x86
878+
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Release|x86.Build.0 = Release|x86
865879
EndGlobalSection
866880
GlobalSection(SolutionProperties) = preSolution
867881
HideSolutionNode = FALSE
@@ -928,6 +942,7 @@ Global
928942
{25ED93D0-A70C-4A07-84D9-EF94115259C9} = {2EF2F7CE-E56F-4B81-A5A5-277693529D43}
929943
{1D7E11AA-27B6-4863-B5EC-1F0ECC6979B2} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
930944
{0077F121-DC2B-425E-A2F4-DEAC2A566E14} = {5C915382-C886-457D-8641-9E766D8E5A17}
945+
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
931946
EndGlobalSection
932947
GlobalSection(ExtensibilityGlobals) = postSolution
933948
SolutionGuid = {160A1D00-1F5B-40F8-A155-621B4459D78F}

Diff for: src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.cpp

+34-12
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ void CorProfiler::RewritingPInvokeMaps(const ModuleMetadata& module_metadata, co
531531
profiler_ref);
532532
if (FAILED(hr))
533533
{
534-
Logger::Warn("ModuleLoadFinished: DefinePinvokeMap to the actual profiler file path "
534+
Logger::Warn("RewritingPInvokeMaps: DefinePinvokeMap to the actual profiler file path "
535535
"failed, trying to restore the previous one.");
536536
hr = metadata_emit->DefinePinvokeMap(methodDef, pdwMappingFlags,
537537
WSTRING(importName).c_str(), importModule);
@@ -540,15 +540,15 @@ void CorProfiler::RewritingPInvokeMaps(const ModuleMetadata& module_metadata, co
540540
// We only warn that we cannot rewrite the PInvokeMap but we still continue the module
541541
// load.
542542
// These errors must be handled on the caller with a try/catch.
543-
Logger::Warn("ModuleLoadFinished: Error trying to restore the previous PInvokeMap.");
543+
Logger::Warn("RewritingPInvokeMaps: Error trying to restore the previous PInvokeMap.");
544544
}
545545
}
546546
}
547547
else
548548
{
549549
// We only warn that we cannot rewrite the PInvokeMap but we still continue the module load.
550550
// These errors must be handled on the caller with a try/catch.
551-
Logger::Warn("ModuleLoadFinished: DeletePinvokeMap failed");
551+
Logger::Warn("RewritingPInvokeMaps: DeletePinvokeMap failed");
552552
}
553553
}
554554

@@ -559,7 +559,7 @@ void CorProfiler::RewritingPInvokeMaps(const ModuleMetadata& module_metadata, co
559559
{
560560
// We only warn that we cannot rewrite the PInvokeMap but we still continue the module load.
561561
// These errors must be handled on the caller with a try/catch.
562-
Logger::Warn("ModuleLoadFinished: Native Profiler DefineModuleRef failed");
562+
Logger::Warn("RewritingPInvokeMaps: Native Profiler DefineModuleRef failed");
563563
}
564564
}
565565
}
@@ -716,9 +716,14 @@ HRESULT STDMETHODCALLTYPE CorProfiler::ModuleLoadFinished(ModuleID module_id, HR
716716
}
717717
}
718718

719-
if (module_info.assembly.name == managed_profiler_name)
719+
#ifdef _WIN32
720+
const bool perform_netfx_redirect = runtime_information_.is_desktop() && IsNetFxAssemblyRedirectionEnabled();
721+
#else
722+
const bool perform_netfx_redirect = false;
723+
#endif // _WIN32
724+
725+
if (perform_netfx_redirect || module_info.assembly.name == managed_profiler_name)
720726
{
721-
// Fix PInvoke Rewriting
722727
ComPtr<IUnknown> metadata_interfaces;
723728
auto hr = this->info_->GetModuleMetaData(module_id, ofRead | ofWrite, IID_IMetaDataImport2,
724729
metadata_interfaces.GetAddressOf());
@@ -740,17 +745,34 @@ HRESULT STDMETHODCALLTYPE CorProfiler::ModuleLoadFinished(ModuleID module_id, HR
740745
module_info.assembly.app_domain_id, &corAssemblyProperty, enable_by_ref_instrumentation,
741746
enable_calltarget_state_by_ref);
742747

743-
const auto& assemblyImport = GetAssemblyImportMetadata(assembly_import);
744-
const auto& assemblyVersion = assemblyImport.version.str();
748+
#ifdef _WIN32
749+
if (perform_netfx_redirect)
750+
{
751+
// On the .NET Framework redirect any assembly reference to the versions required by
752+
// OpenTelemetry.AutoInstrumentation assembly, the ones under netfx/ folder.
753+
RedirectAssemblyReferences(assembly_import, assembly_emit);
754+
}
755+
#endif // _WIN32
745756

746-
Logger::Info("ModuleLoadFinished: ", managed_profiler_name, " v", assemblyVersion, " - Fix PInvoke maps");
757+
if (module_info.assembly.name == managed_profiler_name)
758+
{
747759
#ifdef _WIN32
748-
RewritingPInvokeMaps(module_metadata, windows_nativemethods_type);
760+
RewritingPInvokeMaps(module_metadata, windows_nativemethods_type);
749761
#else
750-
RewritingPInvokeMaps(module_metadata, nonwindows_nativemethods_type);
762+
RewritingPInvokeMaps(module_metadata, nonwindows_nativemethods_type);
751763
#endif // _WIN32
764+
}
765+
766+
if (Logger::IsDebugEnabled())
767+
{
768+
const auto& assemblyImport = GetAssemblyImportMetadata(assembly_import);
769+
const auto& assemblyVersion = assemblyImport.version.str();
770+
771+
Logger::Debug("ModuleLoadFinished: done ", module_info.assembly.name, " v", assemblyVersion);
772+
}
752773
}
753-
else
774+
775+
if (module_info.assembly.name != managed_profiler_name)
754776
{
755777
module_ids_.push_back(module_id);
756778

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// <copyright file="AssemblyRedirectionOnNetFrameworkTests.cs" company="OpenTelemetry Authors">
2+
// Copyright The OpenTelemetry Authors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
// </copyright>
16+
17+
#if NETFRAMEWORK
18+
using FluentAssertions;
19+
using IntegrationTests.Helpers;
20+
using Xunit.Abstractions;
21+
22+
namespace IntegrationTests;
23+
24+
public class AssemblyRedirectionOnNetFrameworkTests : TestHelper
25+
{
26+
public AssemblyRedirectionOnNetFrameworkTests(ITestOutputHelper output)
27+
: base("AssemblyRedirection.NetFramework", output)
28+
{
29+
}
30+
31+
[Fact]
32+
public void SubmitsTraces()
33+
{
34+
using var collector = new MockSpansCollector(Output);
35+
SetExporter(collector);
36+
37+
const string TestApplicationActivitySource = "AssemblyRedirection.NetFramework.ActivitySource";
38+
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES", TestApplicationActivitySource);
39+
collector.Expect(TestApplicationActivitySource);
40+
41+
RunTestApplication();
42+
43+
collector.AssertExpectations();
44+
}
45+
}
46+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Diagnostics;
2+
using System.Reflection;
3+
4+
using var activitySource = new ActivitySource("AssemblyRedirection.NetFramework.ActivitySource");
5+
using var activity = activitySource.StartActivity("AssemblyRedirection.Activity");
6+
7+
Console.WriteLine($"Running {Assembly.GetExecutingAssembly()?.Location}");
8+
9+
var loadedAssemblyNames = new HashSet<string>();
10+
var hasMultipleInstancesOfSingleAssembly = false;
11+
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
12+
Array.Sort<Assembly>(assemblies, (l, r) => l.FullName?.CompareTo(r.FullName) ?? -1);
13+
Console.WriteLine("Loaded assemblies:");
14+
foreach (var assembly in assemblies)
15+
{
16+
var location = assembly.IsDynamic ? "<dynamic>" : assembly.Location;
17+
Console.WriteLine($" + {assembly.FullName}\n\t{location} ");
18+
var assemblyName = assembly.GetName()?.Name ?? string.Empty;
19+
if (!string.IsNullOrWhiteSpace(assemblyName) && !loadedAssemblyNames.Add(assemblyName))
20+
{
21+
hasMultipleInstancesOfSingleAssembly = true;
22+
Console.WriteLine($"\n * WARNING: {assemblyName} loaded more than once.\n");
23+
}
24+
}
25+
26+
Console.WriteLine();
27+
return hasMultipleInstancesOfSingleAssembly ? -1 : 0;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<!-- In order to test assembly redirection this project will force the use of specific versions -->
4+
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
5+
<TargetFramework>net462</TargetFramework>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<!-- WARNING: Keep this version of System.Diagnostics.DiagnosticSource to ensure the test is validating the actual scenario. -->
10+
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="6.0.0" />
11+
</ItemGroup>
12+
</Project>

0 commit comments

Comments
 (0)