-
Notifications
You must be signed in to change notification settings - Fork 153
Add modified WCF CallTarget instrumentation via opt-in environment variable #1992
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
deeb127
cc0fcd9
d6b9d16
e75fdd2
c50d70e
3928831
e529a6b
c5987de
e75305a
a9fabf6
0992cfb
f9b0cf0
75f06c9
c9cc188
d6a0625
f03eda8
9ccf9dd
3357f16
c9dd080
760fef7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -873,7 +873,8 @@ HRESULT CallTargetTokens::ModifyLocalSigAndInitialize(void* rewriterWrapperPtr, | |
|
|
||
| HRESULT CallTargetTokens::WriteBeginMethod(void* rewriterWrapperPtr, mdTypeRef integrationTypeRef, | ||
| const TypeInfo* currentType, | ||
| std::vector<FunctionMethodArgument>& methodArguments, ILInstr** instruction) | ||
| const std::vector<FunctionMethodArgument>& methodArguments, | ||
| const std::vector<USHORT>& argsToLoad, ILInstr** instruction) | ||
| { | ||
| auto hr = EnsureBaseCalltargetTokens(); | ||
| if (FAILED(hr)) | ||
|
|
@@ -884,7 +885,9 @@ HRESULT CallTargetTokens::WriteBeginMethod(void* rewriterWrapperPtr, mdTypeRef i | |
| ILRewriterWrapper* rewriterWrapper = (ILRewriterWrapper*) rewriterWrapperPtr; | ||
| ModuleMetadata* module_metadata = GetMetadata(); | ||
|
|
||
| auto numArguments = (int) methodArguments.size(); | ||
| auto numArgsToLoad = argsToLoad.size(); | ||
| bool useArgToLoad = numArgsToLoad > 0; | ||
| auto numArguments = useArgToLoad ? (int) numArgsToLoad : (int) methodArguments.size(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, just to clarify, changing this doesn't mean we accidentally select the wrong overload, it just means we don't pass those arguments to our instrumentation?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's right in this part of the code we are already rewriting the method, not selecting it. |
||
| if (numArguments >= FASTPATH_COUNT) | ||
| { | ||
| return WriteBeginMethodWithArgumentsArray(rewriterWrapperPtr, integrationTypeRef, currentType, instruction); | ||
|
|
@@ -947,7 +950,17 @@ HRESULT CallTargetTokens::WriteBeginMethod(void* rewriterWrapperPtr, mdTypeRef i | |
| ULONG argumentsSignatureSize[FASTPATH_COUNT]; | ||
| for (auto i = 0; i < numArguments; i++) | ||
| { | ||
| auto signatureSize = methodArguments[i].GetSignature(argumentsSignatureBuffer[i]); | ||
| FunctionMethodArgument methodArgument; | ||
| if (useArgToLoad) | ||
| { | ||
| methodArgument = methodArguments[argsToLoad[i]]; | ||
| } | ||
| else | ||
| { | ||
| methodArgument = methodArguments[i]; | ||
| } | ||
|
|
||
| auto signatureSize = methodArgument.GetSignature(argumentsSignatureBuffer[i]); | ||
| argumentsSignatureSize[i] = signatureSize; | ||
| signatureLength += signatureSize; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| // <copyright file="AsyncMethodInvoker_InvokeBegin_Integration.cs" company="Datadog"> | ||
| // 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. | ||
| // </copyright> | ||
|
|
||
| #if NETFRAMEWORK | ||
| using System; | ||
| using System.ComponentModel; | ||
| using System.Reflection; | ||
| using Datadog.Trace.ClrProfiler.CallTarget; | ||
| using Datadog.Trace.ClrProfiler.Emit; | ||
| using Datadog.Trace.ClrProfiler.Integrations; | ||
| using Datadog.Trace.Configuration; | ||
|
|
||
| namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf | ||
| { | ||
| /// <summary> | ||
| /// System.ServiceModel.Dispatcher.AsyncMethodInvoker calltarget instrumentation | ||
| /// </summary> | ||
| [InstrumentMethod( | ||
| AssemblyName = "System.ServiceModel", | ||
| TypeName = "System.ServiceModel.Dispatcher.AsyncMethodInvoker", | ||
| MethodName = "InvokeBegin", | ||
| ReturnTypeName = ClrNames.IAsyncResult, | ||
| ParameterTypeNames = new[] { ClrNames.Object, "System.Object[]", ClrNames.AsyncCallback, ClrNames.Object }, | ||
| MinimumVersion = "4.0.0", | ||
| MaximumVersion = "4.*.*", | ||
| IntegrationName = WcfCommon.IntegrationName)] | ||
| [Browsable(false)] | ||
| [EditorBrowsable(EditorBrowsableState.Never)] | ||
| public class AsyncMethodInvoker_InvokeBegin_Integration | ||
| { | ||
| /// <summary> | ||
| /// OnMethodBegin callback | ||
| /// </summary> | ||
| /// <typeparam name="TTarget">Type of the target</typeparam> | ||
| /// <param name="instance">Instance value, aka `this` of the instrumented method.</param> | ||
| /// <param name="instanceArg">RequestContext instance</param> | ||
| /// <param name="inputs">Input arguments</param> | ||
| /// <param name="callback">Callback argument</param> | ||
| /// <param name="state">State argument</param> | ||
| /// <returns>Calltarget state value</returns> | ||
| public static CallTargetState OnMethodBegin<TTarget>(TTarget instance, object instanceArg, object[] inputs, AsyncCallback callback, object state) | ||
| { | ||
| // TODO Just use the OperationContext.Current object to get the span information | ||
| // context.IncomingMessageHeaders contains: | ||
| // - Action | ||
| // - To | ||
| // | ||
| // context.IncomingMessageProperties contains: | ||
| // - ["httpRequest"] key to find distributed tracing headers | ||
| if (!Tracer.Instance.Settings.IsIntegrationEnabled(WcfCommon.IntegrationId) || !Tracer.Instance.Settings.DelayWcfInstrumentationEnabled || WcfCommon.GetCurrentOperationContext is null) | ||
| { | ||
| return CallTargetState.GetDefault(); | ||
| } | ||
|
|
||
| var requestContext = WcfCommon.GetCurrentOperationContext()?.GetProperty<object>("RequestContext").GetValueOrDefault(); | ||
| return new CallTargetState(WcfIntegration.CreateScope(requestContext)); | ||
| } | ||
| } | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| // <copyright file="AsyncMethodInvoker_InvokeEnd_Integration.cs" company="Datadog"> | ||
| // 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. | ||
| // </copyright> | ||
|
|
||
| #if NETFRAMEWORK | ||
| using System; | ||
| using System.ComponentModel; | ||
| using System.Reflection; | ||
| using Datadog.Trace.ClrProfiler.CallTarget; | ||
| using Datadog.Trace.Configuration; | ||
|
|
||
| namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf | ||
| { | ||
| /// <summary> | ||
| /// System.ServiceModel.Dispatcher.AsyncMethodInvoker calltarget instrumentation | ||
| /// </summary> | ||
| [InstrumentMethod( | ||
| AssemblyName = "System.ServiceModel", | ||
| TypeName = "System.ServiceModel.Dispatcher.AsyncMethodInvoker", | ||
| MethodName = "InvokeEnd", | ||
| ReturnTypeName = ClrNames.Object, | ||
| ParameterTypeNames = new[] { ClrNames.Object, "System.Object[]&", ClrNames.IAsyncResult }, | ||
| TargetMethodArgumentsToLoad = new ushort[] { 0, 2 }, // DO NOT pass the "out object[]" parameter into the instrumentation method | ||
| MinimumVersion = "4.0.0", | ||
| MaximumVersion = "4.*.*", | ||
| IntegrationName = WcfCommon.IntegrationName)] | ||
| [Browsable(false)] | ||
| [EditorBrowsable(EditorBrowsableState.Never)] | ||
| public class AsyncMethodInvoker_InvokeEnd_Integration | ||
| { | ||
| /// <summary> | ||
| /// OnMethodEnd callback | ||
| /// </summary> | ||
| /// <typeparam name="TTarget">Type of the target</typeparam> | ||
| /// <typeparam name="TReturn">Type of the response</typeparam> | ||
| /// <param name="instance">Instance value, aka `this` of the instrumented method.</param> | ||
| /// <param name="returnValue">Return value</param> | ||
| /// <param name="exception">Exception instance in case the original code threw an exception.</param> | ||
| /// <param name="state">Calltarget state value</param> | ||
| /// <returns>A response value, in an async scenario will be T of Task of T</returns> | ||
| public static CallTargetReturn<TReturn> OnMethodEnd<TTarget, TReturn>(TTarget instance, TReturn returnValue, Exception exception, CallTargetState state) | ||
| { | ||
| var scope = Tracer.Instance.ActiveScope; | ||
| scope.DisposeWithException(exception); | ||
andrewlock marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return new CallTargetReturn<TReturn>(returnValue); | ||
| } | ||
| } | ||
| } | ||
| #endif | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems unlikely, but would we ever want to load
0arguments (i.e. just access the instance)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible, so I added another field to allow us to disambiguate between
0arguments and NOT use the feature in f9b0cf0