diff --git a/tracer/src/Datadog.Trace/Debugger/Helpers/AsyncHelper.cs b/tracer/src/Datadog.Trace/Debugger/Helpers/AsyncHelper.cs index c5adf8891eb7..47ff9bb3279b 100644 --- a/tracer/src/Datadog.Trace/Debugger/Helpers/AsyncHelper.cs +++ b/tracer/src/Datadog.Trace/Debugger/Helpers/AsyncHelper.cs @@ -229,6 +229,28 @@ internal static bool IsAsync(MethodBase method) return method?.DeclaringType?.GetInterfaces().Any(i => i == typeof(IAsyncStateMachine)) == true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ref AsyncDebuggerState GetStateRef(ref object state) + { +#if NETCOREAPP + if (state is not AsyncDebuggerState) + { + state = new AsyncDebuggerState(); + } + + return ref Unsafe.Unbox(state); +#else + if (state is StrongBox strongBox) + { + return ref strongBox.Value; + } + + var newBox = new StrongBox(default); + state = newBox; + return ref newBox.Value; +#endif + } + internal readonly ref struct AsyncKickoffMethodInfo { public AsyncKickoffMethodInfo(object kickoffParentObject, Type kickoffParentType, MethodBase kickoffMethod) diff --git a/tracer/src/Datadog.Trace/Debugger/Instrumentation/AsyncMethodDebuggerInvokerV2.MultiProbe.cs b/tracer/src/Datadog.Trace/Debugger/Instrumentation/AsyncMethodDebuggerInvokerV2.MultiProbe.cs new file mode 100644 index 000000000000..5cd283abbde7 --- /dev/null +++ b/tracer/src/Datadog.Trace/Debugger/Instrumentation/AsyncMethodDebuggerInvokerV2.MultiProbe.cs @@ -0,0 +1,155 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +#nullable enable + +using System; +using System.Runtime.CompilerServices; +using Datadog.Trace.Debugger.Helpers; +using Datadog.Trace.Debugger.Instrumentation.Collections; +using Datadog.Trace.Logging; + +namespace Datadog.Trace.Debugger.Instrumentation +{ + /// + /// Shim of with object type as the state, instead of . + /// To better capture enter/leave of async methods, we inject a field into user's StateMachine types. + /// The injected field used to be that lives inside Datadog.Trace. + /// When our managed loader fails/bail out in loading our assembly, the runtime won't be able to resolve that type + /// resulting in TypeLoadException. To avoid that, we inject `System.Object` as the field instead. + /// In this case, even if we end up not loading Datadog.Trace, nothing bad will happen. + /// + public static class AsyncMethodDebuggerInvokerV2 + { + internal static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(AsyncMethodDebuggerInvokerV2)); + + /// + /// Determines if the instrumentation should call . + /// + /// The unique index of the method. + /// The unique identifier of the instrumentation. + /// state that is used to determine if it's the first entry to the state machine MoveNext method + /// true if should be called, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ShouldUpdateProbeInfo(int methodMetadataIndex, int instrumentationVersion, ref object state) + { + ref var asyncState = ref AsyncHelper.GetStateRef(ref state); + return AsyncMethodDebuggerInvoker.ShouldUpdateProbeInfo(methodMetadataIndex, instrumentationVersion, ref asyncState); + } + + /// + /// Shim on top of . + /// + /// Target object of the method. Note that it could be typeof(object) and not a concrete type + /// Probe Ids + /// Probe Metadata Indices + /// Instance value + /// The unique index of the method. + /// The version of this particular instrumentation. + /// The handle of the executing method + /// The handle of the type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void UpdateProbeInfo( + string[] probeIds, + int[] probeMetadataIndices, + TTarget instance, + int methodMetadataIndex, + int instrumentationVersion, + RuntimeMethodHandle methodHandle, + RuntimeTypeHandle typeHandle) + { + AsyncMethodDebuggerInvoker.UpdateProbeInfo(probeIds, probeMetadataIndices, instance, methodMetadataIndex, instrumentationVersion, methodHandle, typeHandle); + } + + /// + /// Begin Method Invoker + /// + /// Target object of the method. Note that it could be typeof(object) and not a concrete type + /// Instance value + /// The index used to lookup for the associated with the executing method + /// The version of this particular instrumentation. + /// State that is sued to know if it's the first entry to the state machine MoveNext method + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void BeginMethod(TTarget instance, int methodMetadataIndex, int instrumentationVersion, ref object state) + { + ref var asyncState = ref AsyncHelper.GetStateRef(ref state); + AsyncMethodDebuggerInvoker.BeginMethod(instance, methodMetadataIndex, instrumentationVersion, ref asyncState); + } + + /// + /// Logs the given ByRef. + /// + /// Type of local. + /// The local to be logged. + /// index of given argument. + /// Debugger state + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LogLocal(ref TLocal local, int index, ref object state) + { + ref var asyncState = ref AsyncHelper.GetStateRef(ref state); + AsyncMethodDebuggerInvoker.LogLocal(ref local, index, ref asyncState); + } + + /// + /// End Method with void return value + /// This method is called from either (1) the outer-most catch clause when the async method threw exception + /// or (2) when the async method has logically ended. + /// In this phase we have the correct async context in hand because we already set it in the BeginMethod. + /// + /// Target object of the method. Note that it could be typeof(object) and not a concrete type + /// Instance value + /// Exception value + /// Debugger state + /// LiveDebugger return structure + public static DebuggerReturn EndMethod_StartMarker(TTarget instance, Exception exception, ref object state) + { + ref var asyncState = ref AsyncHelper.GetStateRef(ref state); + return AsyncMethodDebuggerInvoker.EndMethod_StartMarker(instance, exception, ref asyncState); + } + + /// + /// End Method with Return value - the MoveNext method always returns void but here we send the kick-off method's return value + /// This method is called from either (1) the outer-most catch clause when the async method threw exception + /// or (2) when the async method has logically ended. + /// In this phase we have the correct async context in hand because we already set it in the BeginMethod. + /// + /// Target object of the method. Note that it could be typeof(object) and not a concrete type + /// Return type + /// Instance value + /// Return value + /// Exception value + /// Debugger asyncState + /// LiveDebugger return structure + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DebuggerReturn EndMethod_StartMarker(TTarget instance, TReturn returnValue, Exception exception, ref object state) + { + ref var asyncState = ref AsyncHelper.GetStateRef(ref state); + return AsyncMethodDebuggerInvoker.EndMethod_StartMarker(instance, returnValue, exception, ref asyncState); + } + + /// + /// End Method with Void return value invoker + /// + /// Debugger state + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void EndMethod_EndMarker(ref object state) + { + ref var asyncState = ref AsyncHelper.GetStateRef(ref state); + AsyncMethodDebuggerInvoker.EndMethod_EndMarker(ref asyncState); + } + + /// + /// Log exception + /// + /// Exception instance + /// Debugger state + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LogException(Exception exception, ref object state) + { + ref var asyncState = ref AsyncHelper.GetStateRef(ref state); + AsyncMethodDebuggerInvoker.LogException(exception, ref asyncState); + } + } +} diff --git a/tracer/src/Datadog.Trace/Debugger/Instrumentation/SpanDebuggerInvoker.cs b/tracer/src/Datadog.Trace/Debugger/Instrumentation/SpanDebuggerInvoker.cs index 69abd3b3ac01..bed888b5b0c5 100644 --- a/tracer/src/Datadog.Trace/Debugger/Instrumentation/SpanDebuggerInvoker.cs +++ b/tracer/src/Datadog.Trace/Debugger/Instrumentation/SpanDebuggerInvoker.cs @@ -11,6 +11,7 @@ using Datadog.Trace.Configuration; using Datadog.Trace.Debugger.Configurations.Models; using Datadog.Trace.Debugger.Expressions; +using Datadog.Trace.Debugger.Helpers; using Datadog.Trace.Debugger.Instrumentation.Collections; using Datadog.Trace.Debugger.RateLimiting; using Datadog.Trace.Logging; @@ -87,6 +88,21 @@ public static void LogException(Exception exception, ref SpanDebuggerState state } } + /// + /// Alternative implementation that was introduced to avoid injection of + /// into user code at bootstrap to avoid `TypeLoadException` if Datadog.Trace fails to be loaded into the app. + /// + /// The id of the probe + /// The resource name + /// The operation name + /// State that is used to know if we are dealing with re-entry to the MoveNext of the async method + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void BeginSpan(string probeId, string resourceName, string operationName, ref object state) + { + ref var asyncState = ref AsyncHelper.GetStateRef(ref state); + BeginSpan(probeId, resourceName, operationName, ref asyncState); + } + /// /// [Async] Begin Method Invoker /// @@ -107,6 +123,19 @@ public static void BeginSpan(string probeId, string resourceName, string operati isReEntryToMoveNext.SpanState = spanState; } + /// + /// Alternative implementation that was introduced to avoid injection of + /// into user code at bootstrap to avoid `TypeLoadException` if Datadog.Trace fails to be loaded into the app. + /// + /// Exception value + /// Debugger state + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void EndSpan(Exception exception, ref object state) + { + ref var asyncState = ref AsyncHelper.GetStateRef(ref state); + EndSpan(exception, ref asyncState); + } + /// /// [Async] End Method with Void return value invoker /// @@ -123,6 +152,19 @@ public static void EndSpan(Exception exception, ref AsyncDebuggerState state) state.SpanState.Value.Scope.DisposeWithException(exception); } + /// + /// Alternative implementation that was introduced to avoid injection of + /// into user code at bootstrap to avoid `TypeLoadException` if Datadog.Trace fails to be loaded into the app. + /// + /// Exception instance + /// Debugger state + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LogException(Exception exception, ref object state) + { + ref var asyncState = ref AsyncHelper.GetStateRef(ref state); + LogException(exception, ref asyncState); + } + /// /// [Async] Log exception /// diff --git a/tracer/src/Datadog.Tracer.Native/debugger_environment_variables.h b/tracer/src/Datadog.Tracer.Native/debugger_environment_variables.h index 74accd53419f..29dc4407d0bd 100644 --- a/tracer/src/Datadog.Tracer.Native/debugger_environment_variables.h +++ b/tracer/src/Datadog.Tracer.Native/debugger_environment_variables.h @@ -16,13 +16,18 @@ namespace environment const WSTRING internal_instrument_all_lines_enabled = WStr("DD_INTERNAL_DEBUGGER_INSTRUMENT_ALL_LINES"); const WSTRING internal_instrument_all_lines_path = WStr("DD_INTERNAL_DEBUGGER_INSTRUMENT_ALL_LINES_PATH"); - // Determines if the Dynamic Instrumentation (aka live debugger) is enabled. + // Determines if the Dynamic Instrumentation (aka live debugger) product is enabled. const WSTRING dynamic_instrumentation_enabled = WStr("DD_DYNAMIC_INSTRUMENTATION_ENABLED"); // Determines if the Exception Replay product is enabled. - const WSTRING exception_debugging_enabled = WStr("DD_EXCEPTION_DEBUGGING_ENABLED"); // Old name const WSTRING exception_replay_enabled = WStr("DD_EXCEPTION_REPLAY_ENABLED"); + // Determines if Dynamic Instrumentation should be controlled by managed activation + const WSTRING dynamic_instrumentation_managed_activation_enabled = WStr("DD_DYNAMIC_INSTRUMENTATION_MANAGED_ACTIVATION_ENABLED"); + + // Determines if Exception Replay should be controlled by managed activation + const WSTRING exception_replay_managed_activation_enabled = WStr("DD_EXCEPTION_REPLAY_MANAGED_ACTIVATION_ENABLED"); + } // namespace environment } // namespace debugger diff --git a/tracer/src/Datadog.Tracer.Native/debugger_environment_variables_util.cpp b/tracer/src/Datadog.Tracer.Native/debugger_environment_variables_util.cpp index 050f09940682..4558353e92a9 100644 --- a/tracer/src/Datadog.Tracer.Native/debugger_environment_variables_util.cpp +++ b/tracer/src/Datadog.Tracer.Native/debugger_environment_variables_util.cpp @@ -11,15 +11,17 @@ bool IsDynamicInstrumentationEnabled() bool IsExceptionReplayEnabled() { - static int sValue = -1; - if (sValue == -1) - { - const auto old_exception_replay_flag = GetEnvironmentValue(environment::exception_debugging_enabled); - const auto new_exception_replay_flag = GetEnvironmentValue(environment::exception_replay_enabled); - sValue = (IsTrue(old_exception_replay_flag) || IsTrue(new_exception_replay_flag)) ? 1 : 0; - } - - return sValue == 1; + CheckIfTrue(GetEnvironmentValue(environment::exception_replay_enabled)); +} + +bool IsDynamicInstrumentationManagedActivationDisabled() +{ + CheckIfFalse(GetEnvironmentValue(environment::dynamic_instrumentation_managed_activation_enabled)); +} + +bool IsExceptionReplayManagedActivationDisabled() +{ + CheckIfFalse(GetEnvironmentValue(environment::exception_replay_managed_activation_enabled)); } bool IsDebuggerInstrumentAllEnabled() diff --git a/tracer/src/Datadog.Tracer.Native/debugger_environment_variables_util.h b/tracer/src/Datadog.Tracer.Native/debugger_environment_variables_util.h index ce4a515a045c..1aa374bcb2ca 100644 --- a/tracer/src/Datadog.Tracer.Native/debugger_environment_variables_util.h +++ b/tracer/src/Datadog.Tracer.Native/debugger_environment_variables_util.h @@ -10,6 +10,8 @@ namespace debugger bool IsDynamicInstrumentationEnabled(); bool IsExceptionReplayEnabled(); +bool IsDynamicInstrumentationManagedActivationDisabled(); +bool IsExceptionReplayManagedActivationDisabled(); bool IsDebuggerInstrumentAllEnabled(); bool IsDebuggerInstrumentAllLinesEnabled(); diff --git a/tracer/src/Datadog.Tracer.Native/debugger_probes_instrumentation_requester.cpp b/tracer/src/Datadog.Tracer.Native/debugger_probes_instrumentation_requester.cpp index ee02617b78db..b5b37f91f655 100644 --- a/tracer/src/Datadog.Tracer.Native/debugger_probes_instrumentation_requester.cpp +++ b/tracer/src/Datadog.Tracer.Native/debugger_probes_instrumentation_requester.cpp @@ -330,19 +330,36 @@ DebuggerProbesInstrumentationRequester::DebuggerProbesInstrumentationRequester( m_work_offloader(work_offloader), m_fault_tolerant_method_duplicator(fault_tolerant_method_duplicator) { - auto diEnabled = IsDynamicInstrumentationEnabled(); + auto diManagedActivationDisabled = IsDynamicInstrumentationManagedActivationDisabled(); + if (diManagedActivationDisabled) + { + Logger::Info("Dynamic Instrumentation Stable Config is explicitly disabled"); + } + + auto diEnabled = !diManagedActivationDisabled || IsDynamicInstrumentationEnabled(); if (diEnabled == false) { - Logger::Debug("Dynamic Instrumentation is disabled"); + Logger::Info("Dynamic Instrumentation hot standby is disabled"); + } + + auto erManagedActivationDisabled = IsExceptionReplayManagedActivationDisabled(); + if (erManagedActivationDisabled) + { + Logger::Info("Exception Replay Stable Config is explicitly disabled"); } - auto erEnabled = IsExceptionReplayEnabled(); + auto erEnabled = !erManagedActivationDisabled || IsExceptionReplayEnabled(); if (erEnabled == false) { - Logger::Debug("Exception Replay is explicitly disabled"); + Logger::Info("Exception Replay hot standby is disabled"); } - is_debugger_or_exception_debugging_enabled = diEnabled || erEnabled; + is_debugger_or_exception_replay_hot_standby = diEnabled || erEnabled; + + if (is_debugger_or_exception_replay_hot_standby) + { + Logger::Info("Dynamic Instrumentation/Exception Replay Hot Standby is on"); + } } void DebuggerProbesInstrumentationRequester::RemoveProbes(debugger::DebuggerRemoveProbesDefinition* removeProbes, @@ -888,17 +905,17 @@ void DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToMod return; } - Logger::Debug("Requesting Rejit for Module: ", moduleInfo.assembly.name); + Logger::Debug("DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToModule: Requesting Rejit for Module: ", moduleInfo.assembly.name); ComPtr metadataInterfaces; - Logger::Debug(" Loading Assembly Metadata..."); + Logger::Debug("DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToModule: Loading Assembly Metadata..."); auto hr = corProfilerInfo->GetModuleMetaData(moduleInfo.id, ofRead | ofWrite, IID_IMetaDataImport2, metadataInterfaces.GetAddressOf()); if (hr != S_OK) { Logger::Warn( - "DebuggerProbesInstrumentationRequester::sAddMetadataToModule failed to get metadata interface for ", + "DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToModule: failed to get metadata interface for ", moduleInfo.id, " ", moduleInfo.assembly.name); return; } @@ -911,7 +928,7 @@ void DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToMod metadataInterfaces.As(IID_IMetaDataAssemblyEmit); std::unique_ptr assemblyMetadata = std::make_unique(GetAssemblyImportMetadata(assemblyImport)); - Logger::Debug(" Assembly Metadata loaded for: ", assemblyMetadata->name, "(", assemblyMetadata->version.str(), + Logger::Debug("DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToModule: Assembly Metadata loaded for: ", assemblyMetadata->name, "(", assemblyMetadata->version.str(), ")."); m_fault_tolerant_method_duplicator->DuplicateAll(moduleId, moduleInfo, metadataImport, metadataEmit); @@ -1001,14 +1018,10 @@ void DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToMod return; } - unsigned callTargetStateBuffer; - auto callTargetStateSize = CorSigCompressToken(asyncMethodDebuggerStateTypeRef, &callTargetStateBuffer); - COR_SIGNATURE fieldSignature[500]; unsigned offset = 0; fieldSignature[offset++] = IMAGE_CEE_CS_CALLCONV_FIELD; - fieldSignature[offset++] = ELEMENT_TYPE_VALUETYPE; - memcpy(&fieldSignature[offset], &callTargetStateBuffer, callTargetStateSize); + fieldSignature[offset++] = ELEMENT_TYPE_OBJECT; mdFieldDef isFirstEntry = mdFieldDefNil; hr = metadataEmit->DefineField(typeDef, managed_profiler_debugger_is_first_entry_field_name.c_str(), @@ -1020,20 +1033,20 @@ void DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToMod "_isFirstEntry failed"); } - Logger::Debug("Added IsFirstEntry field [ModuleId=", moduleInfo.id, ", Assembly=", moduleInfo.assembly.name, + Logger::Debug("DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToModule: Added IsFirstEntry field [ModuleId=", moduleInfo.id, ", Assembly=", moduleInfo.assembly.name, ", Type=", typeInfo.name, ", IsValueType=", typeInfo.valueType, "]"); } } HRESULT STDMETHODCALLTYPE DebuggerProbesInstrumentationRequester::ModuleLoadFinished(const ModuleID moduleId) { - if (!is_debugger_or_exception_debugging_enabled) + if (!is_debugger_or_exception_replay_hot_standby) { - return S_OK; + return S_OK; } // IMPORTANT: The call to `ModuleLoadFinished_AddMetadataToModule` must be in `ModuleLoadFinished` as mutating the - // layout of types is only feasible prior the type is loaded.s + // layout of types is only feasible prior the type is loaded. ModuleLoadFinished_AddMetadataToModule(moduleId); RequestRejitForLoadedModule(moduleId); return S_OK; diff --git a/tracer/src/Datadog.Tracer.Native/debugger_probes_instrumentation_requester.h b/tracer/src/Datadog.Tracer.Native/debugger_probes_instrumentation_requester.h index a7d37dd747e7..71348cc1a7d5 100644 --- a/tracer/src/Datadog.Tracer.Native/debugger_probes_instrumentation_requester.h +++ b/tracer/src/Datadog.Tracer.Native/debugger_probes_instrumentation_requester.h @@ -40,7 +40,7 @@ class DebuggerProbesInstrumentationRequester std::shared_ptr m_rejit_handler = nullptr; std::shared_ptr m_work_offloader = nullptr; std::shared_ptr m_fault_tolerant_method_duplicator = nullptr; - bool is_debugger_or_exception_debugging_enabled = false; + bool is_debugger_or_exception_replay_hot_standby = false; std::unordered_map, std::vector, pair_hash> explorationLineProbes; std::once_flag explorationLinesInitFlag; diff --git a/tracer/src/Datadog.Tracer.Native/debugger_tokens.cpp b/tracer/src/Datadog.Tracer.Native/debugger_tokens.cpp index e649c085f363..7a402297bda2 100644 --- a/tracer/src/Datadog.Tracer.Native/debugger_tokens.cpp +++ b/tracer/src/Datadog.Tracer.Native/debugger_tokens.cpp @@ -49,9 +49,7 @@ HRESULT DebuggerTokens::WriteLogArgOrLocal(void* rewriterWrapperPtr, const TypeS auto callTargetStateSize = CorSigCompressToken(stateTypeRef, &callTargetStateBuffer); const auto isMultiProbe = probeType == NonAsyncMethodMultiProbe; - - unsigned long signatureLength = 10 + callTargetStateSize; - signatureLength += isMultiProbe ? 1 : 0; + const auto isAsyncMethod = probeType == AsyncMethodProbe || probeType == AsyncMethodSpanProbe; COR_SIGNATURE signature[signatureBufferSize]; unsigned offset = 0; @@ -71,17 +69,26 @@ HRESULT DebuggerTokens::WriteLogArgOrLocal(void* rewriterWrapperPtr, const TypeS signature[offset++] = ELEMENT_TYPE_I4; // DebuggerState - signature[offset++] = ELEMENT_TYPE_BYREF; - if (isMultiProbe) + if (isAsyncMethod) { - signature[offset++] = ELEMENT_TYPE_SZARRAY; + // The injected field is of type System.Object + signature[offset++] = ELEMENT_TYPE_BYREF; + signature[offset++] = ELEMENT_TYPE_OBJECT; + } + else + { + signature[offset++] = ELEMENT_TYPE_BYREF; + if (isMultiProbe) + { + signature[offset++] = ELEMENT_TYPE_SZARRAY; + } + signature[offset++] = ELEMENT_TYPE_VALUETYPE; + memcpy(&signature[offset], &callTargetStateBuffer, callTargetStateSize); + offset += callTargetStateSize; } - signature[offset++] = ELEMENT_TYPE_VALUETYPE; - memcpy(&signature[offset], &callTargetStateBuffer, callTargetStateSize); - offset += callTargetStateSize; auto hr = module_metadata->metadata_emit->DefineMemberRef(invokerTypeRef, targetMemberName, signature, - signatureLength, &logArgOrLocalRef); + offset, &logArgOrLocalRef); if (FAILED(hr)) { Logger::Warn("Wrapper ", isArg ? "methodLogArgRef" : "methodLogLocalRef", " could not be defined."); @@ -272,7 +279,7 @@ HRESULT DebuggerTokens::EnsureBaseCalltargetTokens() profilerAssemblyRef, instrumentation_allocator_invoker_name.data(), &rentArrayTypeRef); if (FAILED(hr)) { - Logger::Warn("Wrapper asyncMethodDebuggerInvokerTypeRef could not be defined."); + Logger::Warn("Wrapper rentArrayTypeRef could not be defined."); return hr; } } @@ -437,9 +444,6 @@ HRESULT DebuggerTokens::CreateBeginMethodStartMarkerRefSignature(ProbeType probe bool isAsyncMethod = probeType == AsyncMethodProbe; bool isMultiProbe = probeType == NonAsyncMethodMultiProbe; - unsigned long signatureLength = 7 + callTargetStateSize; - signatureLength += isAsyncMethod ? 3 : 2; - COR_SIGNATURE signature[signatureBufferSize]; unsigned offset = 0; @@ -478,10 +482,9 @@ HRESULT DebuggerTokens::CreateBeginMethodStartMarkerRefSignature(ProbeType probe if (isAsyncMethod) { methodName = managed_profiler_debugger_begin_async_method_name; + // The injected field is of type System.Object signature[offset++] = ELEMENT_TYPE_BYREF; - signature[offset++] = ELEMENT_TYPE_VALUETYPE; - memcpy(&signature[offset], &callTargetStateBuffer, callTargetStateSize); - offset += callTargetStateSize; + signature[offset++] = ELEMENT_TYPE_OBJECT; } else { @@ -490,7 +493,7 @@ HRESULT DebuggerTokens::CreateBeginMethodStartMarkerRefSignature(ProbeType probe return GetMetadata()->metadata_emit->DefineMemberRef( invokerTypeRef, methodName.data(), signature, - signatureLength, &beginMethodRef); + offset, &beginMethodRef); } // endmethod with void return @@ -657,10 +660,7 @@ HRESULT DebuggerTokens::CreateEndMethodStartMarkerRefSignature(ProbeType probeTy const auto callTargetStateSize = CorSigCompressToken(stateTypeRef, &callTargetStateBuffer); const auto isMultiProbe = probeType == NonAsyncMethodMultiProbe; - - auto signatureLength = (isVoid ? 8 : 14) + returnTypeSize + exTypeRefSize + callTargetStateSize; - signatureLength++; // ByRef - signatureLength += isMultiProbe ? 1 : 0; + const auto isAsyncMethod = probeType == AsyncMethodProbe || probeType == AsyncMethodSpanProbe; COR_SIGNATURE signature[signatureBufferSize]; unsigned offset = 0; @@ -703,18 +703,26 @@ HRESULT DebuggerTokens::CreateEndMethodStartMarkerRefSignature(ProbeType probeTy memcpy(&signature[offset], &exTypeRefBuffer, exTypeRefSize); offset += exTypeRefSize; - signature[offset++] = ELEMENT_TYPE_BYREF; - - if (isMultiProbe) + if (isAsyncMethod) { - signature[offset++] = ELEMENT_TYPE_SZARRAY; + // The injected field is of type System.Object + signature[offset++] = ELEMENT_TYPE_BYREF; + signature[offset++] = ELEMENT_TYPE_OBJECT; + } + else + { + signature[offset++] = ELEMENT_TYPE_BYREF; + if (isMultiProbe) + { + signature[offset++] = ELEMENT_TYPE_SZARRAY; + } + signature[offset++] = ELEMENT_TYPE_VALUETYPE; + memcpy(&signature[offset], &callTargetStateBuffer, callTargetStateSize); + offset += callTargetStateSize; } - signature[offset++] = ELEMENT_TYPE_VALUETYPE; - memcpy(&signature[offset], &callTargetStateBuffer, callTargetStateSize); - offset += callTargetStateSize; return GetMetadata()->metadata_emit->DefineMemberRef( - invokerTypeRef, managed_profiler_debugger_endmethod_startmarker_name.data(), signature, signatureLength, + invokerTypeRef, managed_profiler_debugger_endmethod_startmarker_name.data(), signature, offset, &endMethodRef); } @@ -736,6 +744,7 @@ HRESULT DebuggerTokens::WriteLogException(void* rewriterWrapperPtr, ProbeType pr mdTypeRef stateTypeRef = GetDebuggerState(probeType); mdTypeRef methodOrLineTypeRef = GetDebuggerInvoker(probeType); const auto isMultiProbe = probeType == NonAsyncMethodMultiProbe; + const auto isAsyncMethod = probeType == AsyncMethodProbe || probeType == AsyncMethodSpanProbe; unsigned exTypeRefBuffer; auto exTypeRefSize = CorSigCompressToken(exTypeRef, &exTypeRefBuffer); @@ -743,9 +752,6 @@ HRESULT DebuggerTokens::WriteLogException(void* rewriterWrapperPtr, ProbeType pr unsigned callTargetStateBuffer; auto callTargetStateSize = CorSigCompressToken(stateTypeRef, &callTargetStateBuffer); - auto signatureLength = 7 + exTypeRefSize + callTargetStateSize; - signatureLength += isMultiProbe ? 1 : 0; - COR_SIGNATURE signature[signatureBufferSize]; unsigned offset = 0; @@ -758,18 +764,27 @@ HRESULT DebuggerTokens::WriteLogException(void* rewriterWrapperPtr, ProbeType pr offset += exTypeRefSize; // DebuggerState - signature[offset++] = ELEMENT_TYPE_BYREF; - if (isMultiProbe) + if (isAsyncMethod) { - signature[offset++] = ELEMENT_TYPE_SZARRAY; + // The injected field is of type System.Object + signature[offset++] = ELEMENT_TYPE_BYREF; + signature[offset++] = ELEMENT_TYPE_OBJECT; + } + else + { + signature[offset++] = ELEMENT_TYPE_BYREF; + if (isMultiProbe) + { + signature[offset++] = ELEMENT_TYPE_SZARRAY; + } + signature[offset++] = ELEMENT_TYPE_VALUETYPE; + memcpy(&signature[offset], &callTargetStateBuffer, callTargetStateSize); + offset += callTargetStateSize; } - signature[offset++] = ELEMENT_TYPE_VALUETYPE; - memcpy(&signature[offset], &callTargetStateBuffer, callTargetStateSize); - offset += callTargetStateSize; auto hr = module_metadata->metadata_emit->DefineMemberRef(methodOrLineTypeRef, managed_profiler_debugger_logexception_name.data(), - signature, signatureLength, &logExceptionRef); + signature, offset, &logExceptionRef); if (FAILED(hr)) { Logger::Warn("Wrapper methodLogExceptionRef could not be defined."); @@ -807,15 +822,13 @@ HRESULT DebuggerTokens::WriteBeginOrEndMethod_EndMarker(void* rewriterWrapperPtr ModuleMetadata* module_metadata = GetMetadata(); auto [beginOrEndEndMarker, beginOrEndMethodName ] = GetBeginOrEndMethodEndMarker(probeType, isBeginMethod); const auto isMultiProbe = probeType == NonAsyncMethodMultiProbe; + const auto isAsyncMethod= probeType == AsyncMethodProbe || probeType == AsyncMethodSpanProbe; if (beginOrEndEndMarker == mdMemberRefNil) { unsigned callTargetStateBuffer; const auto callTargetStateSize = CorSigCompressToken(stateTypeRef, &callTargetStateBuffer); - unsigned long signatureLength = 5 + callTargetStateSize; - signatureLength += isMultiProbe ? 1 : 0; - COR_SIGNATURE signature[signatureBufferSize]; unsigned offset = 0; @@ -823,19 +836,28 @@ HRESULT DebuggerTokens::WriteBeginOrEndMethod_EndMarker(void* rewriterWrapperPtr signature[offset++] = 0x01; // arguments count signature[offset++] = ELEMENT_TYPE_VOID; - // DebuggerState - signature[offset++] = ELEMENT_TYPE_BYREF; - if (isMultiProbe) + if (isAsyncMethod) { - signature[offset++] = ELEMENT_TYPE_SZARRAY; + // The injected field is of type System.Object + signature[offset++] = ELEMENT_TYPE_BYREF; + signature[offset++] = ELEMENT_TYPE_OBJECT; } + else + { + // DebuggerState + signature[offset++] = ELEMENT_TYPE_BYREF; + if (isMultiProbe) + { + signature[offset++] = ELEMENT_TYPE_SZARRAY; + } - signature[offset++] = ELEMENT_TYPE_VALUETYPE; - memcpy(&signature[offset], &callTargetStateBuffer, callTargetStateSize); - offset += callTargetStateSize; + signature[offset++] = ELEMENT_TYPE_VALUETYPE; + memcpy(&signature[offset], &callTargetStateBuffer, callTargetStateSize); + offset += callTargetStateSize; + } auto hr = module_metadata->metadata_emit->DefineMemberRef(invokerTypeRef, beginOrEndMethodName.c_str(), signature, - signatureLength, &beginOrEndEndMarker); + offset, &beginOrEndEndMarker); if (FAILED(hr)) { Logger::Warn("Wrapper beginMethod could not be defined."); @@ -1058,8 +1080,6 @@ HRESULT DebuggerTokens::WriteBeginSpan(void* rewriterWrapperPtr, const TypeInfo* unsigned callTargetStateBuffer; auto lineStateSize = CorSigCompressToken(stateTypeRef, &callTargetStateBuffer); - unsigned long signatureLength = 6 + lineStateSize + (isAsyncMethod ? 2 : 0); - COR_SIGNATURE signature[signatureBufferSize]; unsigned offset = 0; @@ -1083,14 +1103,13 @@ HRESULT DebuggerTokens::WriteBeginSpan(void* rewriterWrapperPtr, const TypeInfo* if (isAsyncMethod) { + // The injected field is of type System.Object signature[offset++] = ELEMENT_TYPE_BYREF; - signature[offset++] = ELEMENT_TYPE_VALUETYPE; - memcpy(&signature[offset], &callTargetStateBuffer, lineStateSize); - offset += lineStateSize; + signature[offset++] = ELEMENT_TYPE_OBJECT; } auto hr = module_metadata->metadata_emit->DefineMemberRef( - lineTypeRef, managed_profiler_debugger_begin_span_name.data(), signature, signatureLength, &beginLineRef); + lineTypeRef, managed_profiler_debugger_begin_span_name.data(), signature, offset, &beginLineRef); if (FAILED(hr)) { @@ -1131,8 +1150,6 @@ HRESULT DebuggerTokens::WriteEndSpan(void* rewriterWrapperPtr, ILInstr** instruc unsigned exTypeRefBuffer; const auto exTypeRefSize = CorSigCompressToken(exTypeRef, &exTypeRefBuffer); - unsigned long signatureLength = 6 + callTargetStateSize + exTypeRefSize; - COR_SIGNATURE signature[signatureBufferSize]; unsigned offset = 0; @@ -1144,14 +1161,23 @@ HRESULT DebuggerTokens::WriteEndSpan(void* rewriterWrapperPtr, ILInstr** instruc memcpy(&signature[offset], &exTypeRefBuffer, exTypeRefSize); offset += exTypeRefSize; - // DebuggerState - signature[offset++] = ELEMENT_TYPE_BYREF; - signature[offset++] = ELEMENT_TYPE_VALUETYPE; - memcpy(&signature[offset], &callTargetStateBuffer, callTargetStateSize); - offset += callTargetStateSize; + if (isAsyncMethod) + { + // The injected field is of type System.Object + signature[offset++] = ELEMENT_TYPE_BYREF; + signature[offset++] = ELEMENT_TYPE_OBJECT; + } + else + { + // DebuggerState + signature[offset++] = ELEMENT_TYPE_BYREF; + signature[offset++] = ELEMENT_TYPE_VALUETYPE; + memcpy(&signature[offset], &callTargetStateBuffer, callTargetStateSize); + offset += callTargetStateSize; + } auto hr = module_metadata->metadata_emit->DefineMemberRef( - lineTypeRef, managed_profiler_debugger_end_span_name.data(), signature, signatureLength, &endSpanRef); + lineTypeRef, managed_profiler_debugger_end_span_name.data(), signature, offset, &endSpanRef); if (FAILED(hr)) { Logger::Warn("Wrapper beginMethod could not be defined."); @@ -1185,11 +1211,6 @@ HRESULT DebuggerTokens::WriteShouldUpdateProbeInfo(void* rewriterWrapperPtr, ILI { auto [invokerTypeRef, stateTypeRef] = GetDebuggerInvokerAndState(isAsyncMethod ? AsyncMethodProbe : probeType); - unsigned callTargetStateBuffer; - auto callTargetStateSize = CorSigCompressToken(stateTypeRef, &callTargetStateBuffer); - - unsigned long signatureLength = 5 + (isAsyncMethod ? 2 + callTargetStateSize : 0); - COR_SIGNATURE signature[signatureBufferSize]; unsigned offset = 0; @@ -1202,14 +1223,13 @@ HRESULT DebuggerTokens::WriteShouldUpdateProbeInfo(void* rewriterWrapperPtr, ILI if (isAsyncMethod) { + // The injected field is of type System.Object signature[offset++] = ELEMENT_TYPE_BYREF; - signature[offset++] = ELEMENT_TYPE_VALUETYPE; - memcpy(&signature[offset], &callTargetStateBuffer, callTargetStateSize); - offset += callTargetStateSize; + signature[offset++] = ELEMENT_TYPE_OBJECT; } auto hr = module_metadata->metadata_emit->DefineMemberRef( - invokerTypeRef, managed_profiler_debugger_should_update_probe_info_name.data(), signature, signatureLength, + invokerTypeRef, managed_profiler_debugger_should_update_probe_info_name.data(), signature, offset, &shouldUpdateProbeInfoRef); if (FAILED(hr)) { @@ -1496,9 +1516,6 @@ HRESULT DebuggerTokens::WriteDispose(void* rewriterWrapperPtr, ILInstr** instruc const auto isMultiProbe = probeType == NonAsyncMethodMultiProbe; - unsigned long signatureLength = 7 + callTargetStateSize; - signatureLength += isMultiProbe ? 1 : 0; - COR_SIGNATURE signature[signatureBufferSize]; unsigned offset = 0; @@ -1520,7 +1537,7 @@ HRESULT DebuggerTokens::WriteDispose(void* rewriterWrapperPtr, ILInstr** instruc offset += callTargetStateSize; auto hr = module_metadata->metadata_emit->DefineMemberRef( - typeRef, managed_profiler_debugger_dispose_name.data(), signature, signatureLength, + typeRef, managed_profiler_debugger_dispose_name.data(), signature, offset, &disposeRef); if (FAILED(hr)) { diff --git a/tracer/src/Datadog.Tracer.Native/debugger_tokens.h b/tracer/src/Datadog.Tracer.Native/debugger_tokens.h index 4390ffe0f77b..00e831f9db6a 100644 --- a/tracer/src/Datadog.Tracer.Native/debugger_tokens.h +++ b/tracer/src/Datadog.Tracer.Native/debugger_tokens.h @@ -66,7 +66,7 @@ static const WSTRING managed_profiler_debugger_async_linestatetype = WStr("Datad static const WSTRING managed_profiler_debugger_is_first_entry_field_name = WStr("<>dd_liveDebugger_isReEntryToMoveNext"); static const WSTRING managed_profiler_debugger_async_method_debugger_state_field_name = WStr("LogState"); // See `Datadog.Trace.Debugger.Instrumentation.AsyncDebuggerState.LogState` static const WSTRING managed_profiler_debugger_begin_async_method_name = WStr("BeginMethod"); -static const WSTRING managed_profiler_debugger_async_method_invoker_type = WStr("Datadog.Trace.Debugger.Instrumentation.AsyncMethodDebuggerInvoker"); +static const WSTRING managed_profiler_debugger_async_method_invoker_type = WStr("Datadog.Trace.Debugger.Instrumentation.AsyncMethodDebuggerInvokerV2"); static const WSTRING managed_profiler_debugger_async_method_state_type = WStr("Datadog.Trace.Debugger.Instrumentation.AsyncDebuggerState"); // Span probe