Skip to content

Commit 99befb1

Browse files
authored
[Dynamic Instrumentation] Perf improvements + Added probe metadata payload (#3725)
* Deleted all the ConcurrentDictionary that played part in the hot path of our DI's instrumentation * Added a test with probe that is getting changed between different phases + fixed an issue we had with duplicated probes in the native side * Cleaned singleton enforcement of the registries * Fixed a bug we had where we changed the TaskLocal of the continuation and did not restore it upon rate limiting hit * Fixed misinstrumetation of async methods with stack-allocated return values * attached span_id & trace_id to debugger snapshots * attaching tags from ProbeDefintion into Debugger Snapshots * Addressed PR comments
1 parent 89c2f27 commit 99befb1

File tree

44 files changed

+1683
-341
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1683
-341
lines changed

tracer/src/Datadog.Trace/Debugger/Expressions/ProbeExpressionsProcessor.cs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,6 @@ internal static ProbeExpressionsProcessor Instance
3535
}
3636
}
3737

38-
internal ProbeInfo? GetProbeInfo(string probeId)
39-
{
40-
return Get(probeId)?.ProbeInfo;
41-
}
42-
43-
internal bool Process<T>(string probeId, ref CaptureInfo<T> info, DebuggerSnapshotCreator snapshotCreator)
44-
{
45-
var probeProcessor = Get(probeId);
46-
if (probeProcessor == null)
47-
{
48-
Log.Error("Probe processor has not found. Probe Id: " + probeId);
49-
return false;
50-
}
51-
52-
return probeProcessor.Process(ref info, snapshotCreator);
53-
}
54-
5538
internal void AddProbeProcessor(ProbeDefinition probe)
5639
{
5740
try
@@ -72,7 +55,7 @@ internal void Remove(string probeId)
7255
_processors.TryRemove(probeId, out _);
7356
}
7457

75-
private ProbeProcessor Get(string probeId)
58+
internal ProbeProcessor Get(string probeId)
7659
{
7760
_processors.TryGetValue(probeId, out var probeProcessor);
7861
return probeProcessor;

tracer/src/Datadog.Trace/Debugger/Expressions/ProbeInfo.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ internal readonly record struct ProbeInfo(
1212
ProbeType ProbeType,
1313
ProbeLocation ProbeLocation,
1414
EvaluateAt EvaluateAt,
15-
bool HasCondition)
15+
bool HasCondition,
16+
string[] Tags)
1617
{
1718
internal string ProbeId { get; } = ProbeId;
1819

@@ -25,5 +26,7 @@ internal readonly record struct ProbeInfo(
2526
internal bool IsFullSnapshot { get; } = ProbeType == ProbeType.Snapshot;
2627

2728
internal bool HasCondition { get; } = HasCondition;
29+
30+
internal string[] Tags { get; } = Tags;
2831
}
2932
}

tracer/src/Datadog.Trace/Debugger/Expressions/ProbeProcessor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ internal ProbeProcessor(ProbeDefinition probe)
5858
probeType,
5959
location,
6060
evaluateAt,
61-
HasCondition());
61+
HasCondition(),
62+
probe.Tags);
6263
}
6364

6465
internal ProbeInfo ProbeInfo { get; }

tracer/src/Datadog.Trace/Debugger/Instrumentation/AsyncLineDebuggerInvoker.cs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Runtime.CompilerServices;
99
using Datadog.Trace.Debugger.Expressions;
1010
using Datadog.Trace.Debugger.Helpers;
11+
using Datadog.Trace.Debugger.Instrumentation.Registry;
1112
using Datadog.Trace.Debugger.RateLimiting;
1213
using Datadog.Trace.Logging;
1314

@@ -53,7 +54,8 @@ public static void LogLocal<TLocal>(ref TLocal local, int index, ref AsyncLineDe
5354
}
5455

5556
var captureInfo = new CaptureInfo<TLocal>(value: local, type: typeof(TLocal), methodState: MethodState.LogLocal, name: localName, memberKind: ScopeMemberKind.Local);
56-
if (!ProbeExpressionsProcessor.Instance.Process(state.ProbeId, ref captureInfo, state.SnapshotCreator))
57+
58+
if (!state.ProbeData.Processor.Process(ref captureInfo, state.SnapshotCreator))
5759
{
5860
state.IsActive = false;
5961
}
@@ -104,6 +106,7 @@ public static void LogException(Exception exception, ref AsyncLineDebuggerState
104106
/// </summary>
105107
/// <typeparam name="TTarget">Target type</typeparam>
106108
/// <param name="probeId">The id of the probe</param>
109+
/// <param name="probeMetadataIndex">The index used to lookup for the <see cref="ProbeData"/></param>
107110
/// <param name="instance">Instance value</param>
108111
/// <param name="methodHandle">The handle of the executing method</param>
109112
/// <param name="typeHandle">The handle of the type</param>
@@ -112,7 +115,7 @@ public static void LogException(Exception exception, ref AsyncLineDebuggerState
112115
/// <param name="probeFilePath">The path to the file of the probe</param>
113116
/// <returns>Live debugger state</returns>
114117
[MethodImpl(MethodImplOptions.AggressiveInlining)]
115-
public static AsyncLineDebuggerState BeginLine<TTarget>(string probeId, TTarget instance, RuntimeMethodHandle methodHandle, RuntimeTypeHandle typeHandle, int methodMetadataIndex, int lineNumber, string probeFilePath)
118+
public static AsyncLineDebuggerState BeginLine<TTarget>(string probeId, int probeMetadataIndex, TTarget instance, RuntimeMethodHandle methodHandle, RuntimeTypeHandle typeHandle, int methodMetadataIndex, int lineNumber, string probeFilePath)
116119
{
117120
try
118121
{
@@ -125,7 +128,7 @@ public static AsyncLineDebuggerState BeginLine<TTarget>(string probeId, TTarget
125128
}
126129

127130
// Assess if we have metadata associated with the given index
128-
if (!MethodMetadataProvider.IsIndexExists(methodMetadataIndex))
131+
if (!MethodMetadataCollection.Instance.IsIndexExists(methodMetadataIndex))
129132
{
130133
// State machine is null when we run in Optimized code and the original async method was generic,
131134
// in which case the state machine is a generic value type.
@@ -137,25 +140,32 @@ public static AsyncLineDebuggerState BeginLine<TTarget>(string probeId, TTarget
137140
Log.Warning($"{nameof(BeginLine)}: hoisted 'this' has not found. {kickoffInfo.KickoffParentType.Name}.{kickoffInfo.KickoffMethod.Name}");
138141
}
139142

140-
if (!MethodMetadataProvider.TryCreateAsyncMethodMetadataIfNotExists(instance, methodMetadataIndex, in methodHandle, in typeHandle, kickoffInfo))
143+
if (!MethodMetadataCollection.Instance.TryCreateAsyncMethodMetadataIfNotExists(instance, methodMetadataIndex, in methodHandle, in typeHandle, kickoffInfo))
141144
{
142-
Log.Warning($"BeginMethod_StartMarker: Failed to receive the InstrumentedMethodInfo associated with the executing method. type = {typeof(TTarget)}, instance type name = {instance.GetType().Name}, methodMetadataId = {methodMetadataIndex}");
145+
Log.Warning($"([Async]BeginLine: Failed to receive the InstrumentedMethodInfo associated with the executing method. type = {typeof(TTarget)}, instance type name = {instance.GetType().Name}, methodMetadataId = {methodMetadataIndex}, probeId = {probeId}");
143146
return CreateInvalidatedAsyncLineDebuggerState();
144147
}
145148
}
146149

150+
if (!ProbeMetadataCollection.Instance.TryCreateProbeMetadataIfNotExists(probeMetadataIndex, probeId))
151+
{
152+
Log.Warning($"[Async]BeginLine: Failed to receive the ProbeData associated with the executing probe. type = {typeof(TTarget)}, instance type name = {instance?.GetType().Name}, probeMetadataIndex = {probeMetadataIndex}, probeId = {probeId}");
153+
return CreateInvalidatedAsyncLineDebuggerState();
154+
}
155+
147156
var kickoffParentObject = AsyncHelper.GetAsyncKickoffThisObject(instance);
148-
var state = new AsyncLineDebuggerState(probeId, scope: default, DateTimeOffset.UtcNow, methodMetadataIndex, lineNumber, probeFilePath, instance, kickoffParentObject);
157+
var state = new AsyncLineDebuggerState(probeId, scope: default, DateTimeOffset.UtcNow, methodMetadataIndex, probeMetadataIndex, lineNumber, probeFilePath, instance, kickoffParentObject);
149158

150159
if (!state.SnapshotCreator.ProbeHasCondition &&
151-
!ProbeRateLimiter.Instance.Sample(probeId))
160+
!state.ProbeData.Sampler.Sample())
152161
{
153162
return CreateInvalidatedAsyncLineDebuggerState();
154163
}
155164

156165
var asyncInfo = new AsyncCaptureInfo(state.MoveNextInvocationTarget, state.KickoffInvocationTarget, state.MethodMetadataInfo.KickoffInvocationTargetType, hoistedLocals: state.MethodMetadataInfo.AsyncMethodHoistedLocals, hoistedArgs: state.MethodMetadataInfo.AsyncMethodHoistedArguments);
157166
var captureInfo = new CaptureInfo<Type>(value: null, type: state.MethodMetadataInfo.DeclaringType, methodState: MethodState.BeginLineAsync, localsCount: state.MethodMetadataInfo.LocalVariableNames.Length, argumentsCount: state.MethodMetadataInfo.ParameterNames.Length, lineCaptureInfo: new LineCaptureInfo(lineNumber, probeFilePath), asyncCaptureInfo: asyncInfo);
158-
if (!ProbeExpressionsProcessor.Instance.Process(probeId, ref captureInfo, state.SnapshotCreator))
167+
168+
if (!state.ProbeData.Processor.Process(ref captureInfo, state.SnapshotCreator))
159169
{
160170
state.IsActive = false;
161171
}
@@ -191,7 +201,7 @@ public static void EndLine(ref AsyncLineDebuggerState state)
191201
state.HasLocalsOrReturnValue = false;
192202
var asyncCaptureInfo = new AsyncCaptureInfo(state.MoveNextInvocationTarget, state.KickoffInvocationTarget, state.MethodMetadataInfo.KickoffInvocationTargetType, kickoffMethod: state.MethodMetadataInfo.KickoffMethod, hoistedArgs: state.MethodMetadataInfo.AsyncMethodHoistedArguments, hoistedLocals: state.MethodMetadataInfo.AsyncMethodHoistedLocals);
193203
var captureInfo = new CaptureInfo<object>(memberKind: ScopeMemberKind.This, methodState: MethodState.EndLineAsync, hasLocalOrArgument: hasArgumentsOrLocals, asyncCaptureInfo: asyncCaptureInfo, lineCaptureInfo: new LineCaptureInfo(state.LineNumber, state.ProbeFilePath));
194-
ProbeExpressionsProcessor.Instance.Process(state.ProbeId, ref captureInfo, state.SnapshotCreator);
204+
state.ProbeData.Processor.Process(ref captureInfo, state.SnapshotCreator);
195205
}
196206
catch (Exception e)
197207
{

tracer/src/Datadog.Trace/Debugger/Instrumentation/AsyncLineDebuggerState.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
// <copyright file="AsyncLineDebuggerState.cs" company="Datadog">
1+
// <copyright file="AsyncLineDebuggerState.cs" company="Datadog">
22
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
33
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
44
// </copyright>
55

66
using System;
77
using System.ComponentModel;
88
using System.Runtime.CompilerServices;
9+
using Datadog.Trace.Debugger.Instrumentation.Registry;
910
using Datadog.Trace.Debugger.Snapshots;
1011

1112
namespace Datadog.Trace.Debugger.Instrumentation
@@ -24,7 +25,7 @@ public ref struct AsyncLineDebuggerState
2425
private readonly int _lineNumber;
2526

2627
/// <summary>
27-
/// Used to perform a fast lookup to grab the proper <see cref="Instrumentation.MethodMetadataInfo"/>.
28+
/// Used to perform a fast lookup to grab the proper <see cref="Registry.MethodMetadataInfo"/>.
2829
/// This index is hard-coded into the method's instrumented bytecode.
2930
/// </summary>
3031
private readonly int _methodMetadataIndex;
@@ -43,12 +44,13 @@ public ref struct AsyncLineDebuggerState
4344
/// <param name="probeId">The id of the probe</param>
4445
/// <param name="scope">Scope instance</param>
4546
/// <param name="startTime">The timestamp captured when the relevant line of code was hit</param>
46-
/// <param name="methodMetadataIndex">The unique index of the method's <see cref="Instrumentation.MethodMetadataInfo"/></param>
47+
/// <param name="methodMetadataIndex">The unique index of the method's <see cref="Registry.MethodMetadataInfo"/></param>
48+
/// <param name="probeMetadataIndex">The unique index of the probe <see cref="ProbeData"/></param>
4749
/// <param name="lineNumber">The line number where the probe is located on</param>
4850
/// <param name="probeFilePath">The path to the file of the probe</param>
4951
/// <param name="invocationTarget">The instance object (or null for static methods)</param>
5052
/// <param name="kickoffInvocationTarget">The instance object (or null for static methods) of the kickoff method</param>
51-
internal AsyncLineDebuggerState(string probeId, Scope scope, DateTimeOffset? startTime, int methodMetadataIndex, int lineNumber, string probeFilePath, object invocationTarget, object kickoffInvocationTarget)
53+
internal AsyncLineDebuggerState(string probeId, Scope scope, DateTimeOffset? startTime, int methodMetadataIndex, int probeMetadataIndex, int lineNumber, string probeFilePath, object invocationTarget, object kickoffInvocationTarget)
5254
{
5355
_probeId = probeId;
5456
_scope = scope;
@@ -57,12 +59,15 @@ internal AsyncLineDebuggerState(string probeId, Scope scope, DateTimeOffset? sta
5759
_lineNumber = lineNumber;
5860
_probeFilePath = probeFilePath;
5961
HasLocalsOrReturnValue = false;
60-
SnapshotCreator = DebuggerSnapshotCreator.BuildSnapshotCreator(probeId);
62+
ProbeData = ProbeMetadataCollection.Instance.Get(probeMetadataIndex);
63+
SnapshotCreator = DebuggerSnapshotCreator.BuildSnapshotCreator(ProbeData.Processor);
6164
_moveNextInvocationTarget = invocationTarget;
6265
_kickoffInvocationTarget = kickoffInvocationTarget;
6366
}
6467

65-
internal ref MethodMetadataInfo MethodMetadataInfo => ref MethodMetadataProvider.Get(_methodMetadataIndex);
68+
internal ref MethodMetadataInfo MethodMetadataInfo => ref MethodMetadataCollection.Instance.Get(_methodMetadataIndex);
69+
70+
internal ProbeData ProbeData { get; }
6671

6772
/// <summary>
6873
/// Gets the LiveDebugger SnapshotCreator

0 commit comments

Comments
 (0)