Skip to content

Commit

Permalink
Update single step guard rails to send telemetry via forwarder
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewlock committed May 31, 2024
1 parent ba57dc1 commit c34915e
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ class EnvironmentVariables final
// DD_INJECTION_ENABLED is non-empty when SSI is enabled
inline static const shared::WSTRING SingleStepInstrumentationEnabled = WStr("DD_INJECTION_ENABLED");
inline static const shared::WSTRING ForceEolInstrumentation = WStr("DD_TRACE_ALLOW_UNSUPPORTED_SSI_RUNTIMES");
inline static const shared::WSTRING SingleStepInstrumentationTelemetryForwarderPath = WStr("DD_TELEMETRY_FORWARDER_PATH");
};
6 changes: 6 additions & 0 deletions shared/src/Datadog.Trace.ClrProfiler.Native/cor_profiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ namespace datadog::shared::nativeloader
if (FAILED(hr))
{
Log::Warn("CorProfiler::Initialize: Failed to attach profiler, interface ICorProfilerInfo4 not found.");
// we're not recording the exact version here, we just know that at this point it's not enough
single_step_guard_rails.RecordBootstrapError(single_step_guard_rails.NetFrameworkRuntime, inferredVersion, "incompatible_runtime");
return E_FAIL;
}
const auto runtimeInformation = GetRuntimeVersion(info4, inferredVersion);
Expand All @@ -258,6 +260,7 @@ namespace datadog::shared::nativeloader
//
if (m_dispatcher == nullptr)
{
single_step_guard_rails.RecordBootstrapError(runtimeInformation, "initialization_error");
return E_FAIL;
}
IDynamicInstance* cpInstance = m_dispatcher->GetContinuousProfilerInstance();
Expand Down Expand Up @@ -321,6 +324,7 @@ namespace datadog::shared::nativeloader
if (FAILED(hr))
{
Log::Warn("CorProfiler::Initialize: Error getting the event mask.");
single_step_guard_rails.RecordBootstrapError(runtimeInformation, "initialization_error");
return E_FAIL;
}

Expand Down Expand Up @@ -486,9 +490,11 @@ namespace datadog::shared::nativeloader
if (FAILED(hr))
{
Log::Warn("CorProfiler::Initialize: Error setting the event mask.");
single_step_guard_rails.RecordBootstrapError(runtimeInformation, "initialization_error");
return E_FAIL;
}

single_step_guard_rails.RecordBootstrapSuccess(runtimeInformation);
return S_OK;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "../../../shared/src/native-src/version.h"
#include "EnvironmentVariables.h"
#include "single_step_guard_rails.h"
#include "process_helper.h"

using namespace shared;

Expand Down Expand Up @@ -122,6 +123,7 @@ HRESULT SingleStepGuardRails::HandleUnsupportedNetCoreVersion(const std::string&
EnvironmentVariables::ForceEolInstrumentation,
"=1 to override this check and force instrumentation");

SendAbortTelemetry(NetCoreRuntime, runtimeVersion, MinNetCoreVersion, MaxNetCoreVersion);
return E_FAIL;
}

Expand All @@ -139,6 +141,7 @@ HRESULT SingleStepGuardRails::HandleUnsupportedNetFrameworkVersion(const std::st
EnvironmentVariables::ForceEolInstrumentation,
"=1 to override this check and force instrumentation");

SendAbortTelemetry(NetFrameworkRuntime, runtimeVersion, MinNetFrameworkVersion, MaxNetFrameworkVersion);
return E_FAIL;
}

Expand All @@ -163,4 +166,132 @@ bool SingleStepGuardRails::ShouldForceInstrumentationOverride(const std::string&

return false;
}

void SingleStepGuardRails::RecordBootstrapError(const RuntimeInformation& runtimeInformation,
const std::string& errorType) const
{
if(!m_isRunningInSingleStep)
{
return;
}

const auto runtimeName = runtimeInformation.is_core() ? NetCoreRuntime : NetFrameworkRuntime;
const auto runtimeVersion = runtimeInformation.description();

RecordBootstrapError(runtimeName, runtimeVersion, errorType);
}

void SingleStepGuardRails::RecordBootstrapError(const std::string& runtimeName, const std::string& runtimeVersion,
const std::string& errorType) const
{
if(!m_isRunningInSingleStep)
{
return;
}

const std::string points = "[{\\\"name\\\": \\\"library_entrypoint.error\\\", \\\"tags\\\": [\\\"error_type:"
+ errorType + "\\\"]}]";
SendTelemetry(runtimeName, runtimeVersion, points);
}

void SingleStepGuardRails::RecordBootstrapSuccess(const RuntimeInformation& runtimeInformation) const
{
Log::Debug("SingleStepGuardRails::RecordBootstrapSuccess");
if(!m_isRunningInSingleStep)
{
Log::Debug("SingleStepGuardRails::RecordBootstrapSuccess Not running in Single Step mode");
return;
}

Log::Debug("SingleStepGuardRails::RecordBootstrapSuccess Generating telemetry");
const auto runtimeName = runtimeInformation.is_core() ? NetCoreRuntime : NetFrameworkRuntime;
const auto runtimeVersion = runtimeInformation.description();

const std::string isForced = m_isForcedExecution ? "true" : "false";
const std::string points = "[{\\\"name\\\": \\\"library_entrypoint.complete\\\", \\\"tags\\\": [\\\"injection_forced:"
+ isForced + "\\\"]}]";

SendTelemetry(runtimeName, runtimeVersion, points);
}

void SingleStepGuardRails::SendAbortTelemetry(const std::string& runtimeName, const std::string& runtimeVersion,
const std::string& minVersion, const std::string& maxVersion) const
{
if(!m_isRunningInSingleStep)
{
return;
}

const std::string reason = "eol_runtime"; // possible reasons [”eol_runtime”,””incompatible_runtime”, ”integration”, ”package_manager”]

const std::string abort = "{\\\"name\\\": \\\"library_entrypoint.abort\\\", \\\"tags\\\": [\\\"reason:"
+ reason + "\\\"]}";
const std::string abort_runtime =
"{\\\"name\\\": \\\"library_entrypoint.abort.runtime\\\", \\\"tags\\\": [\\\"min_supported_version:"
+ minVersion + "\\\",\\\"max_supported_version:" + maxVersion + "\\\"]}";

const std::string points = "[" + abort + "," + abort_runtime + "]";

SendTelemetry(runtimeName, runtimeVersion, points);
}

void SingleStepGuardRails::SendTelemetry(const std::string& runtimeName, const std::string& runtimeVersion,
const std::string& points) const
{
if(!m_isRunningInSingleStep)
{
Log::Debug("SingleStepGuardRails::SendTelemetry Not running in single step mode");
return;
}

auto forwarderPath = GetEnvironmentValue(EnvironmentVariables::SingleStepInstrumentationTelemetryForwarderPath);
if (forwarderPath.empty())
{
Log::Info("SingleStepGuardRails::SendTelemetry: Unable to send telemetry, ",
EnvironmentVariables::SingleStepInstrumentationTelemetryForwarderPath, " is not set");
return;
}

std::error_code ec; // fs::exists might throw if no error_code parameter is provided
if (!fs::exists(forwarderPath, ec))
{
Log::Info("SingleStepGuardRails::SendTelemetry: Unable to send telemetry, ",
EnvironmentVariables::SingleStepInstrumentationTelemetryForwarderPath, " path does not exist:",
forwarderPath);
return;
}

const std::string metadata =
"{\\\"metadata\\\":{\\\"runtime_name\\\": \\\"" + runtimeName
+ "\\\",\\\"runtime_version\\\": \\\"" + runtimeVersion
+ "\\\",\\\"language_name\\\": \\\"dotnet\\\",\\\"language_version\\\": \\\"" + runtimeVersion
+ "\\\",\\\"tracer_name\\\": \\\"dotnet\\\",\\\"tracer_version\\\": \\\"" + PROFILER_VERSION
+ "\\\",\\\"pid\\\":" + std::to_string(GetPID())
+ ",},\\\"points\\\": " + points + "}";


// We only pass through DD_TRACE_LOG_DIRECTORY for testing purposes, it's not required by the telemetry forwarder
const auto logDir = "DD_TRACE_LOG_DIRECTORY=" + ToString(GetEnvironmentValue(WStr("DD_TRACE_LOG_DIRECTORY")));
const std::vector env = {logDir};
const auto processPath = ToString(forwarderPath);

#ifdef _WIN32
const std::vector args = {metadata};
#else
// linux and mac require different escaping, so just regex remove the \ for now
const auto linuxMetadata = std::regex_replace(metadata, std::regex("\\\\"), "");
const std::vector args = {linuxMetadata};
#endif

Log::Debug("SingleStepGuardRails::SendTelemetry: Invoking: ", processPath, " with ", args[0]);
const auto success = ProcessHelper::RunProcess(processPath, args, env);
if(success)
{
Log::Debug("SingleStepGuardRails::SendTelemetry: Telemetry sent to forwarder");
}
else
{
Log::Warn("SingleStepGuardRails::SendTelemetry: Error calling telemetry forwarder");
}
}
} // namespace datadog::shared::nativeloader
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@ class SingleStepGuardRails
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);

void SendAbortTelemetry(const std::string& runtimeName, const std::string& runtimeVersion, const std::string& minVersion, const std::string& maxVersion) const;
void SendTelemetry(const std::string& runtimeName, const std::string& runtimeVersion, const std::string& telemetryPoints) const;
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);
void RecordBootstrapError(const std::string& runtimeName, const std::string& runtimeVersion, const std::string& errorType) const;
void RecordBootstrapError(const RuntimeInformation& runtimeInformation, const std::string& errorType) const;
void RecordBootstrapSuccess(const RuntimeInformation& runtimeInformation) const;

};
} // namespace datadog::shared::nativeloader

0 comments on commit c34915e

Please sign in to comment.