Skip to content

Commit

Permalink
Extract single-step guard rails work to separate class (as getting a …
Browse files Browse the repository at this point in the history
…bit over the top)
  • Loading branch information
andrewlock committed May 31, 2024
1 parent d9277cf commit 86f00c4
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 75 deletions.
1 change: 1 addition & 0 deletions shared/src/Datadog.Trace.ClrProfiler.Native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ endif()
add_library("Datadog.Trace.ClrProfiler.Native.static" STATIC
cor_profiler.cpp
process_helper.cpp
single_step_guard_rails.cpp
cor_profiler_class_factory.cpp
dynamic_dispatcher.cpp
dynamic_instance.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@
<ClInclude Include="process_helper.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="runtimeid_store.h" />
<ClInclude Include="single_step_guard_rails.h" />
<ClInclude Include="util.h" />
</ItemGroup>
<ItemGroup>
Expand All @@ -355,6 +356,7 @@
<ClCompile Include="exported_functions.cpp" />
<ClCompile Include="process_helper.cpp" />
<ClCompile Include="runtimeid_store.cpp" />
<ClCompile Include="single_step_guard_rails.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="Datadog.Trace.ClrProfiler.Native.def" />
Expand Down
81 changes: 6 additions & 75 deletions shared/src/Datadog.Trace.ClrProfiler.Native/cor_profiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "util.h"
#include "../../../shared/src/native-src/pal.h"
#include "EnvironmentVariables.h"
#include "single_step_guard_rails.h"
#include "instrumented_assembly_generator/instrumented_assembly_generator_cor_profiler_function_control.h"
#include "instrumented_assembly_generator/instrumented_assembly_generator_cor_profiler_info.h"
#include "instrumented_assembly_generator/instrumented_assembly_generator_helper.h"
Expand Down Expand Up @@ -232,6 +233,8 @@ namespace datadog::shared::nativeloader
}
}

SingleStepGuardRails single_step_guard_rails;

//
// Get Profiler interface ICorProfilerInfo4
//
Expand All @@ -244,82 +247,10 @@ namespace datadog::shared::nativeloader
}
const auto runtimeInformation = GetRuntimeVersion(info4, inferredVersion);

//
// Check if we're running in Single Step, and if so, whether we should bail out
//
// This variable is non-empty when we're in single step
const auto isSingleStepVariable = GetEnvironmentValue(EnvironmentVariables::SingleStepInstrumentationEnabled);
if(!isSingleStepVariable.empty())
if(single_step_guard_rails.CheckRuntime(runtimeInformation, pICorProfilerInfoUnk) != S_OK)
{
Log::Debug("CorProfiler::Initialize: Single step instrumentation detected, checking for EOL environment");
// We're doing single-step instrumentation, check if we're in an EOL environment
IUnknown* tstVerProfilerInfo;
const char* unsupportedFramework = nullptr;
if (runtimeInformation.is_core())
{
// For .NET Core, we require .NET Core 3.1 or higher
// We could use runtime_information, but I don't know how reliable the values we get from that are?
if (S_OK == pICorProfilerInfoUnk->QueryInterface(__uuidof(ICorProfilerInfo11), (void**) &tstVerProfilerInfo))
{
// .NET Core 3.1+, but is it _too_ high?
if(runtimeInformation.major_version > 8)
{
unsupportedFramework = ".NET 9 or higher";
}

// supported
tstVerProfilerInfo->Release();
}
else
{
unsupportedFramework = ".NET Core 3.0 or lower";
}
}
else
{
// For .NET Framework, we require .NET Framework 4.6.1 or higher
if (S_OK == pICorProfilerInfoUnk->QueryInterface(__uuidof(ICorProfilerInfo7), (void**) &tstVerProfilerInfo))
{
// supported
tstVerProfilerInfo->Release();
}
else
{
unsupportedFramework = ".NET Framework 4.6.0 or lower";
}
}

if(unsupportedFramework != nullptr)
{
// Are we supposed to override the EOL check?
const auto forceEolInstrumentationVariable = GetEnvironmentValue(EnvironmentVariables::ForceEolInstrumentation);
bool forceEolInstrumentation;
if(!forceEolInstrumentationVariable.empty()
&& TryParseBooleanEnvironmentValue(forceEolInstrumentationVariable, forceEolInstrumentation)
&& forceEolInstrumentation)
{
Log::Info(
"CorProfiler::Initialize: Unsupported framework version '",
unsupportedFramework,
"' detected. Forcing instrumentation with single-step instrumentation due to ",
EnvironmentVariables::ForceEolInstrumentation);
}
else
{
Log::Warn(
"CorProfiler::Initialize: Single-step instrumentation is not supported in '",
unsupportedFramework,
"'. Set ",
EnvironmentVariables::ForceEolInstrumentation,
" to override this check and force instrumentation");
info4->Release();
return E_FAIL;
}
}
else
{
Log::Debug("CorProfiler::Initialize: Supported framework version detected, continuing with single-step instrumentation");
}
info4->Release();
return E_FAIL;
}

//
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#include "cor_profiler.h"
#include "log.h"
#include "util.h"
#include "../../../shared/src/native-src/version.h"
#include "EnvironmentVariables.h"
#include "single_step_guard_rails.h"

using namespace shared;

namespace datadog::shared::nativeloader
{
SingleStepGuardRails::SingleStepGuardRails()
{
// This variable is non-empty when we're in single step
Log::Debug("SingleStepGuardRails::CheckRuntime: Checking for Single step instrumentation environment using ",
EnvironmentVariables::SingleStepInstrumentationEnabled);
const auto isSingleStepVariable = GetEnvironmentValue(EnvironmentVariables::SingleStepInstrumentationEnabled);

m_isRunningInSingleStep = !isSingleStepVariable.empty();
}

SingleStepGuardRails::~SingleStepGuardRails()
{
}

/**
* Check if we're running in Single Step, and if so, whether we should bail out
*
* @param runtimeInformation
* @param pICorProfilerInfoUnk
* @return
*/
HRESULT SingleStepGuardRails::CheckRuntime(const RuntimeInformation& runtimeInformation, IUnknown* pICorProfilerInfoUnk)
{
//
// Check if we're running in Single Step, and if so, whether we should bail out
//
if(!m_isRunningInSingleStep)
{
// not single step, don't do anything else
return S_OK;
}

Log::Debug("SingleStepGuardRails::CheckRuntime: Single step instrumentation detected, checking for EOL environment");
// We're doing single-step instrumentation, check if we're in an EOL environment

IUnknown* tstVerProfilerInfo;
std::string unsupportedRuntimeVersion;
std::string unsupportedSummary;

if (runtimeInformation.is_core())
{
Log::Debug("SingleStepGuardRails::CheckRuntime: Running in .NET Core - checking available version");

// For .NET Core, we require .NET Core 3.1 or higher
// We could use runtime_information, but I don't know how reliable the values we get from that are?
if (S_OK == pICorProfilerInfoUnk->QueryInterface(__uuidof(ICorProfilerInfo11), (void**) &tstVerProfilerInfo))
{
tstVerProfilerInfo->Release();

// .NET Core 3.1+, but is it _too_ high?
if(runtimeInformation.major_version <= 8)
{
// supported
Log::Debug("SingleStepGuardRails::CheckRuntime: Supported .NET runtime version detected, continuing with single step instrumentation");
return S_OK;
}

const auto eol = ".NET 9 or higher";

const auto runtime = std::to_string(runtimeInformation.major_version) + "." +
std::to_string(runtimeInformation.minor_version) + "." +
std::to_string(runtimeInformation.build_version);
return HandleUnsupportedNetCoreVersion(eol, runtime);
}

// Unsupported EOL version, but _which_ version
// unfortunately we can't trust the major_version etc values here
const auto eol = ".NET Core 3.0 or lower";

if (S_OK == pICorProfilerInfoUnk->QueryInterface(__uuidof(ICorProfilerInfo10), (void**) &tstVerProfilerInfo))
{
// we can't get this exact runtime version from cor profiler unfortunately
tstVerProfilerInfo->Release();
return HandleUnsupportedNetCoreVersion(eol, "3.0.0");
}

if (S_OK == pICorProfilerInfoUnk->QueryInterface(__uuidof(ICorProfilerInfo9), (void**) &tstVerProfilerInfo))
{
// no way of detecting 2.2 from here AFAIK, so we treat 2.1 and 2.2 the same
tstVerProfilerInfo->Release();
return HandleUnsupportedNetCoreVersion(eol, "2.1.0");
}

// Only remaining EOL version that we can instrument is 2.0.0
return HandleUnsupportedNetCoreVersion(eol, "2.0.0");
}

// For .NET Framework, we require .NET Framework 4.6.1 or higher
if (S_OK == pICorProfilerInfoUnk->QueryInterface(__uuidof(ICorProfilerInfo7), (void**) &tstVerProfilerInfo))
{
// supported
tstVerProfilerInfo->Release();
Log::Debug("SingleStepGuardRails::CheckRuntime: Supported .NET Framework runtime version detected, continuing with single step instrumentation");
return S_OK;
}

return HandleUnsupportedNetFrameworkVersion(".NET Framework 4.6.0 or lower", "4.6.0");
}

HRESULT SingleStepGuardRails::HandleUnsupportedNetCoreVersion(const std::string& unsupportedDescription, const std::string& runtimeVersion)
{
if(ShouldForceInstrumentationOverride(unsupportedDescription))
{
return S_OK;
}

Log::Warn(
"CorProfiler::Initialize: Single-step instrumentation is not supported in '",
unsupportedDescription,
"'. Set ",
EnvironmentVariables::ForceEolInstrumentation,
"=1 to override this check and force instrumentation");

return E_FAIL;
}

HRESULT SingleStepGuardRails::HandleUnsupportedNetFrameworkVersion(const std::string& unsupportedDescription, const std::string& runtimeVersion)
{
if(ShouldForceInstrumentationOverride(unsupportedDescription))
{
return S_OK;
}

Log::Warn(
"CorProfiler::Initialize: Single-step instrumentation is not supported in '",
unsupportedDescription,
"'. Set ",
EnvironmentVariables::ForceEolInstrumentation,
"=1 to override this check and force instrumentation");

return E_FAIL;
}

bool SingleStepGuardRails::ShouldForceInstrumentationOverride(const std::string& eolDescription)
{
// Are we supposed to override the EOL check?
const auto forceEolInstrumentationVariable = GetEnvironmentValue(EnvironmentVariables::ForceEolInstrumentation);

bool forceEolInstrumentation;
if (!forceEolInstrumentationVariable.empty()
&& TryParseBooleanEnvironmentValue(forceEolInstrumentationVariable, forceEolInstrumentation)
&& forceEolInstrumentation)
{
m_isForcedExecution = true;
Log::Info(
"CorProfiler::Initialize: Unsupported framework version '",
eolDescription,
"' detected. Forcing instrumentation with single-step instrumentation due to ",
EnvironmentVariables::ForceEolInstrumentation);
return true;
}

return false;
}
} // namespace datadog::shared::nativeloader
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once
#include "../../../shared/src/native-src/com_ptr.h"
#include <string>
#include "cor_profiler.h"

namespace datadog::shared::nativeloader
{
class SingleStepGuardRails
{
private:
bool m_isRunningInSingleStep;
bool m_isForcedExecution = false;

inline static const std::string MinNetFrameworkVersion = "4.6.1";
inline static const std::string MaxNetFrameworkVersion = "4.8.1";
inline static const std::string MinNetCoreVersion = "3.1.0";
inline static const std::string MaxNetCoreVersion = "8.0.0";

bool ShouldForceInstrumentationOverride(const std::string& eolDescription);
HRESULT HandleUnsupportedNetCoreVersion(const std::string& unsupportedDescription, const std::string& runtimeVersion);
HRESULT HandleUnsupportedNetFrameworkVersion(const std::string& unsupportedDescription, const std::string& runtimeVersion);
public:
inline static const std::string NetFrameworkRuntime = ".NET Framework";
inline static const std::string NetCoreRuntime = ".NET Core";

SingleStepGuardRails();
~SingleStepGuardRails();
HRESULT CheckRuntime(const RuntimeInformation& runtimeInformation, IUnknown* pICorProfilerInfoUnk);
};
} // namespace datadog::shared::nativeloader

0 comments on commit 86f00c4

Please sign in to comment.