diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Windows/Datadog.Profiler.Native.def b/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Windows/Datadog.Profiler.Native.def index 2285fa195cd8..c28f78181c54 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Windows/Datadog.Profiler.Native.def +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native.Windows/Datadog.Profiler.Native.def @@ -9,6 +9,7 @@ SetApplicationInfoForAppDomain PRIVATE SetEndpointForTrace PRIVATE SetGitMetadataForApplication PRIVATE + SetProcessTagsForApplication PRIVATE FlushProfile PRIVATE CreateCrashReport PRIVATE SetConfiguration PRIVATE \ No newline at end of file diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ApplicationInfo.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ApplicationInfo.h index 85f8bdc44e8f..2ccd69206a90 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ApplicationInfo.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ApplicationInfo.h @@ -10,4 +10,5 @@ struct ApplicationInfo std::string Version; std::string RepositoryUrl; std::string CommitSha; + std::string ProcessTags; }; diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ApplicationStore.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ApplicationStore.cpp index bd0b821bdfcc..e29b0c7c2688 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ApplicationStore.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ApplicationStore.cpp @@ -65,6 +65,13 @@ void ApplicationStore::SetGitMetadata(std::string runtimeId, std::string reposit // no need to create worker, it has already been created } +void ApplicationStore::SetProcessTags(std::string runtimeId, std::string processTags) +{ + std::lock_guard lock(_infosLock); + auto& info = _infos[std::move(runtimeId)]; + info.ProcessTags = std::move(processTags); +} + const char* ApplicationStore::GetName() { return _serviceName; diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ApplicationStore.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ApplicationStore.h index bfc03f0f1462..965198a412d5 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ApplicationStore.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ApplicationStore.h @@ -30,6 +30,7 @@ class ApplicationStore ApplicationInfo GetApplicationInfo(const std::string& runtimeId) override; void SetApplicationInfo(const std::string& runtimeId, const std::string& serviceName, const std::string& environment, const std::string& version) override; void SetGitMetadata(std::string runtimeId, std::string repositoryUrl, std::string commitSha) override; + void SetProcessTags(std::string runtimeId, std::string processTags) override; const char* GetName() override; diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/IApplicationStore.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/IApplicationStore.h index e79167232af0..c743288135ff 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/IApplicationStore.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/IApplicationStore.h @@ -14,4 +14,5 @@ class IApplicationStore virtual void SetApplicationInfo(const std::string& runtimeId, const std::string& serviceName, const std::string& environment, const std::string& version) = 0; virtual void SetGitMetadata(std::string runtimeId, std::string repositoryUrl, std::string commitSha) = 0; + virtual void SetProcessTags(std::string runtimeId, std::string processTags) = 0; }; \ No newline at end of file diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/PInvoke.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/PInvoke.cpp index 5b1220fcc5f7..f9cab537d1dd 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/PInvoke.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/PInvoke.cpp @@ -170,6 +170,33 @@ extern "C" void __stdcall SetGitMetadataForApplication(const char* runtimeId, co ); } +extern "C" void __stdcall SetProcessTagsForApplication(const char* runtimeId, const char* processTags) +{ + const auto profiler = CorProfilerCallback::GetInstance(); + + if (profiler == nullptr) + { + Log::Error("SetProcessTagsForApplication is called BEFORE CLR initialize"); + return; + } + + if (!profiler->GetClrLifetime()->IsRunning()) + { + return; + } + + if (runtimeId == nullptr) + { + Log::Error("SetProcessTagsForApplication was called with an empty runtime id"); + return; + } + + profiler->GetApplicationStore()->SetProcessTags( + runtimeId, + processTags != nullptr ? processTags : std::string() + ); +} + extern "C" void __stdcall FlushProfile() { const auto profiler = CorProfilerCallback::GetInstance(); diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ProfileExporter.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ProfileExporter.cpp index 2a8da8fccf23..6b6365c5a1d3 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ProfileExporter.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ProfileExporter.cpp @@ -645,6 +645,10 @@ bool ProfileExporter::Export(bool lastCall) { additionalTags.Add("git.commit.sha", applicationInfo.CommitSha); } + if (!applicationInfo.ProcessTags.empty()) + { + additionalTags.Add("_dd.tags.process", applicationInfo.ProcessTags); + } auto filesToSend = std::vector>{}; diff --git a/tracer/src/Datadog.Trace/ContinuousProfiler/NativeInterop.cs b/tracer/src/Datadog.Trace/ContinuousProfiler/NativeInterop.cs index 78dd21b3fe12..3dc60fdf26cc 100644 --- a/tracer/src/Datadog.Trace/ContinuousProfiler/NativeInterop.cs +++ b/tracer/src/Datadog.Trace/ContinuousProfiler/NativeInterop.cs @@ -48,6 +48,12 @@ public static void SetGitMetadata(string runtimeId, string repositoryUrl, string NativeMethods.SetGitMetadata(runtimeId, repositoryUrl, commitSha); } + [MethodImpl(MethodImplOptions.NoInlining)] + public static void SetProcessTags(string runtimeId, string processTagsValue) + { + NativeMethods.SetProcessTags(runtimeId, processTagsValue); + } + [MethodImpl(MethodImplOptions.NoInlining)] public static void FlushProfile() { @@ -103,6 +109,9 @@ private static class NativeMethods [DllImport(dllName: "Datadog.Profiler.Native", EntryPoint = "SetGitMetadataForApplication")] public static extern void SetGitMetadata(string runtimeId, string repositoryUrl, string commitSha); + [DllImport(dllName: "Datadog.Profiler.Native", EntryPoint = "SetProcessTagsForApplication")] + public static extern void SetProcessTags(string runtimeId, string processTagsValue); + [DllImport(dllName: "Datadog.Profiler.Native", EntryPoint = "FlushProfile")] public static extern void FlushProfile(); diff --git a/tracer/src/Datadog.Trace/ProcessTags.cs b/tracer/src/Datadog.Trace/ProcessTags.cs index 699f98396bc3..d6e80cf23d45 100644 --- a/tracer/src/Datadog.Trace/ProcessTags.cs +++ b/tracer/src/Datadog.Trace/ProcessTags.cs @@ -8,8 +8,9 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text; using Datadog.Trace.Configuration; +using Datadog.Trace.ContinuousProfiler; +using Datadog.Trace.Logging; using Datadog.Trace.Processors; using Datadog.Trace.Util; @@ -17,6 +18,8 @@ namespace Datadog.Trace; internal static class ProcessTags { + private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(ProcessTags)); + public const string EntrypointName = "entrypoint.name"; public const string EntrypointBasedir = "entrypoint.basedir"; public const string EntrypointWorkdir = "entrypoint.workdir"; @@ -60,7 +63,12 @@ private static string GetSerializedTags() } serializedTags.Remove(serializedTags.Length - 1, length: 1); // remove last comma - return StringBuilderCache.GetStringAndRelease(serializedTags); + var tagsValues = StringBuilderCache.GetStringAndRelease(serializedTags); + + // also send the tags to the profiler to tag profiles + PropagateProcessTagsToProfiler(tagsValues); + + return tagsValues; } private static string NormalizeTagValue(string tagValue) @@ -69,4 +77,20 @@ private static string NormalizeTagValue(string tagValue) // which we don't want because we use it as a key/value separator. return TraceUtil.NormalizeTag(tagValue).Replace(oldChar: ':', newChar: '_'); } + + private static void PropagateProcessTagsToProfiler(string values) + { + try + { + // Avoid P/Invoke if the profiler is not ready + if (Profiler.Instance.Status.IsProfilerReady && !string.IsNullOrEmpty(values)) + { + NativeInterop.SetProcessTags(RuntimeId.Get(), values); + } + } + catch (Exception ex) + { + Log.Warning(ex, "Failed to share process tags with the Continuous Profiler."); + } + } }