Skip to content

Commit

Permalink
Restore assembly redirection for .NET Framework (#2479)
Browse files Browse the repository at this point in the history
  • Loading branch information
pjanotti committed Apr 19, 2023
1 parent 07cc775 commit bb56733
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 13 deletions.
17 changes: 16 additions & 1 deletion OpenTelemetry.AutoInstrumentation.sln
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication.SelfContain
EndProject
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}"
EndProject
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}"
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}"
EndProject
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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -862,6 +864,18 @@ Global
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Release|x64.Build.0 = Release|Any CPU
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Release|x86.ActiveCfg = Release|Any CPU
{0077F121-DC2B-425E-A2F4-DEAC2A566E14}.Release|x86.Build.0 = Release|Any CPU
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Debug|Any CPU.ActiveCfg = Debug|x64
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Debug|Any CPU.Build.0 = Debug|x64
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Debug|x64.ActiveCfg = Debug|x64
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Debug|x64.Build.0 = Debug|x64
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Debug|x86.ActiveCfg = Debug|x86
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Debug|x86.Build.0 = Debug|x86
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Release|Any CPU.ActiveCfg = Release|x64
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Release|Any CPU.Build.0 = Release|x64
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Release|x64.ActiveCfg = Release|x64
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Release|x64.Build.0 = Release|x64
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Release|x86.ActiveCfg = Release|x86
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -928,6 +942,7 @@ Global
{25ED93D0-A70C-4A07-84D9-EF94115259C9} = {2EF2F7CE-E56F-4B81-A5A5-277693529D43}
{1D7E11AA-27B6-4863-B5EC-1F0ECC6979B2} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
{0077F121-DC2B-425E-A2F4-DEAC2A566E14} = {5C915382-C886-457D-8641-9E766D8E5A17}
{66FB648E-7FAF-418B-B2F6-B7CB9EE579CA} = {E409ADD3-9574-465C-AB09-4324D205CC7C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {160A1D00-1F5B-40F8-A155-621B4459D78F}
Expand Down
46 changes: 34 additions & 12 deletions src/OpenTelemetry.AutoInstrumentation.Native/cor_profiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ void CorProfiler::RewritingPInvokeMaps(const ModuleMetadata& module_metadata, co
profiler_ref);
if (FAILED(hr))
{
Logger::Warn("ModuleLoadFinished: DefinePinvokeMap to the actual profiler file path "
Logger::Warn("RewritingPInvokeMaps: DefinePinvokeMap to the actual profiler file path "
"failed, trying to restore the previous one.");
hr = metadata_emit->DefinePinvokeMap(methodDef, pdwMappingFlags,
WSTRING(importName).c_str(), importModule);
Expand All @@ -540,15 +540,15 @@ void CorProfiler::RewritingPInvokeMaps(const ModuleMetadata& module_metadata, co
// We only warn that we cannot rewrite the PInvokeMap but we still continue the module
// load.
// These errors must be handled on the caller with a try/catch.
Logger::Warn("ModuleLoadFinished: Error trying to restore the previous PInvokeMap.");
Logger::Warn("RewritingPInvokeMaps: Error trying to restore the previous PInvokeMap.");
}
}
}
else
{
// We only warn that we cannot rewrite the PInvokeMap but we still continue the module load.
// These errors must be handled on the caller with a try/catch.
Logger::Warn("ModuleLoadFinished: DeletePinvokeMap failed");
Logger::Warn("RewritingPInvokeMaps: DeletePinvokeMap failed");
}
}

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

if (module_info.assembly.name == managed_profiler_name)
#ifdef _WIN32
const bool perform_netfx_redirect = runtime_information_.is_desktop() && IsNetFxAssemblyRedirectionEnabled();
#else
const bool perform_netfx_redirect = false;
#endif // _WIN32

if (perform_netfx_redirect || module_info.assembly.name == managed_profiler_name)
{
// Fix PInvoke Rewriting
ComPtr<IUnknown> metadata_interfaces;
auto hr = this->info_->GetModuleMetaData(module_id, ofRead | ofWrite, IID_IMetaDataImport2,
metadata_interfaces.GetAddressOf());
Expand All @@ -740,17 +745,34 @@ HRESULT STDMETHODCALLTYPE CorProfiler::ModuleLoadFinished(ModuleID module_id, HR
module_info.assembly.app_domain_id, &corAssemblyProperty, enable_by_ref_instrumentation,
enable_calltarget_state_by_ref);

const auto& assemblyImport = GetAssemblyImportMetadata(assembly_import);
const auto& assemblyVersion = assemblyImport.version.str();
#ifdef _WIN32
if (perform_netfx_redirect)
{
// On the .NET Framework redirect any assembly reference to the versions required by
// OpenTelemetry.AutoInstrumentation assembly, the ones under netfx/ folder.
RedirectAssemblyReferences(assembly_import, assembly_emit);
}
#endif // _WIN32

Logger::Info("ModuleLoadFinished: ", managed_profiler_name, " v", assemblyVersion, " - Fix PInvoke maps");
if (module_info.assembly.name == managed_profiler_name)
{
#ifdef _WIN32
RewritingPInvokeMaps(module_metadata, windows_nativemethods_type);
RewritingPInvokeMaps(module_metadata, windows_nativemethods_type);
#else
RewritingPInvokeMaps(module_metadata, nonwindows_nativemethods_type);
RewritingPInvokeMaps(module_metadata, nonwindows_nativemethods_type);
#endif // _WIN32
}

if (Logger::IsDebugEnabled())
{
const auto& assemblyImport = GetAssemblyImportMetadata(assembly_import);
const auto& assemblyVersion = assemblyImport.version.str();

Logger::Debug("ModuleLoadFinished: done ", module_info.assembly.name, " v", assemblyVersion);
}
}
else

if (module_info.assembly.name != managed_profiler_name)
{
module_ids_.push_back(module_id);

Expand Down
46 changes: 46 additions & 0 deletions test/IntegrationTests/AssemblyRedirectionOnNetFrameworkTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// <copyright file="AssemblyRedirectionOnNetFrameworkTests.cs" company="OpenTelemetry Authors">
// 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.
// </copyright>

#if NETFRAMEWORK
using FluentAssertions;
using IntegrationTests.Helpers;
using Xunit.Abstractions;

namespace IntegrationTests;

public class AssemblyRedirectionOnNetFrameworkTests : TestHelper
{
public AssemblyRedirectionOnNetFrameworkTests(ITestOutputHelper output)
: base("AssemblyRedirection.NetFramework", output)
{
}

[Fact]
public void SubmitsTraces()
{
using var collector = new MockSpansCollector(Output);
SetExporter(collector);

const string TestApplicationActivitySource = "AssemblyRedirection.NetFramework.ActivitySource";
SetEnvironmentVariable("OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES", TestApplicationActivitySource);
collector.Expect(TestApplicationActivitySource);

RunTestApplication();

collector.AssertExpectations();
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Diagnostics;
using System.Reflection;

using var activitySource = new ActivitySource("AssemblyRedirection.NetFramework.ActivitySource");
using var activity = activitySource.StartActivity("AssemblyRedirection.Activity");

Console.WriteLine($"Running {Assembly.GetExecutingAssembly()?.Location}");

var loadedAssemblyNames = new HashSet<string>();
var hasMultipleInstancesOfSingleAssembly = false;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
Array.Sort<Assembly>(assemblies, (l, r) => l.FullName?.CompareTo(r.FullName) ?? -1);
Console.WriteLine("Loaded assemblies:");
foreach (var assembly in assemblies)
{
var location = assembly.IsDynamic ? "<dynamic>" : assembly.Location;
Console.WriteLine($" + {assembly.FullName}\n\t{location} ");
var assemblyName = assembly.GetName()?.Name ?? string.Empty;
if (!string.IsNullOrWhiteSpace(assemblyName) && !loadedAssemblyNames.Add(assemblyName))
{
hasMultipleInstancesOfSingleAssembly = true;
Console.WriteLine($"\n * WARNING: {assemblyName} loaded more than once.\n");
}
}

Console.WriteLine();
return hasMultipleInstancesOfSingleAssembly ? -1 : 0;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- In order to test assembly redirection this project will force the use of specific versions -->
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
<TargetFramework>net462</TargetFramework>
</PropertyGroup>

<ItemGroup>
<!-- WARNING: Keep this version of System.Diagnostics.DiagnosticSource to ensure the test is validating the actual scenario. -->
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="6.0.0" />
</ItemGroup>
</Project>

0 comments on commit bb56733

Please sign in to comment.