diff --git a/shared/src/Datadog.Trace.ClrProfiler.Native/EnvironmentVariables.h b/shared/src/Datadog.Trace.ClrProfiler.Native/EnvironmentVariables.h index 233ca680c976..82b1fad7ea5e 100644 --- a/shared/src/Datadog.Trace.ClrProfiler.Native/EnvironmentVariables.h +++ b/shared/src/Datadog.Trace.ClrProfiler.Native/EnvironmentVariables.h @@ -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"); }; \ No newline at end of file diff --git a/shared/src/Datadog.Trace.ClrProfiler.Native/cor_profiler.cpp b/shared/src/Datadog.Trace.ClrProfiler.Native/cor_profiler.cpp index e51ce70af37f..bd2db9beeb68 100644 --- a/shared/src/Datadog.Trace.ClrProfiler.Native/cor_profiler.cpp +++ b/shared/src/Datadog.Trace.ClrProfiler.Native/cor_profiler.cpp @@ -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); @@ -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(); @@ -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; } @@ -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; } diff --git a/shared/src/Datadog.Trace.ClrProfiler.Native/single_step_guard_rails.cpp b/shared/src/Datadog.Trace.ClrProfiler.Native/single_step_guard_rails.cpp index 5412abf6d9ad..2e710a716c55 100644 --- a/shared/src/Datadog.Trace.ClrProfiler.Native/single_step_guard_rails.cpp +++ b/shared/src/Datadog.Trace.ClrProfiler.Native/single_step_guard_rails.cpp @@ -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; @@ -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; } @@ -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; } @@ -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 \ No newline at end of file diff --git a/shared/src/Datadog.Trace.ClrProfiler.Native/single_step_guard_rails.h b/shared/src/Datadog.Trace.ClrProfiler.Native/single_step_guard_rails.h index c01ce13391d3..2fa2a2831dda 100644 --- a/shared/src/Datadog.Trace.ClrProfiler.Native/single_step_guard_rails.h +++ b/shared/src/Datadog.Trace.ClrProfiler.Native/single_step_guard_rails.h @@ -19,6 +19,9 @@ 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"; @@ -26,5 +29,9 @@ class SingleStepGuardRails 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 \ No newline at end of file