Skip to content
Merged
42 changes: 34 additions & 8 deletions tracer/src/Datadog.Tracer.Native/calltarget_tokens.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,9 +358,9 @@ mdMethodSpec CallTargetTokens::GetCallTargetDefaultValueMethodSpec(const TypeSig
}

HRESULT CallTargetTokens::ModifyLocalSig(ILRewriter* reWriter, TypeSignature* methodReturnValue,
std::vector<TypeSignature>* methodTypeArguments,
std::vector<TypeSignature>* methodTypeArguments, FunctionInfo* caller,
ULONG* callTargetStateIndex, ULONG* exceptionIndex,
ULONG* callTargetReturnIndex, ULONG* returnValueIndex,
ULONG* callTargetReturnIndex, ULONG* staticValueTypeIndex, ULONG* returnValueIndex,
mdToken* callTargetStateToken, mdToken* exceptionToken,
mdToken* callTargetReturnToken, std::vector<ULONG>& additionalLocalIndices, bool isAsyncMethod)
{
Expand Down Expand Up @@ -399,7 +399,17 @@ HRESULT CallTargetTokens::ModifyLocalSig(ILRewriter* reWriter, TypeSignature* me
}
constexpr int variableNumber = 3;
const auto additionalLocalsCount = GetAdditionalLocalsCount(*methodTypeArguments);
ULONG newLocalsCount = variableNumber + additionalLocalsCount;

// For instrumenting static methods in value types, see if we can get the TypeRef for the type.
// If so, include add a local variable with the value type in the signature.
bool isValueType = caller->type.valueType;
bool isStaticValueType = isValueType && !(caller->method_signature.CallingConvention() & IMAGE_CEE_CS_CALLCONV_HASTHIS) && GetCurrentTypeRef(&caller->type, isValueType) != mdTokenNil;

ULONG newLocalsCount = variableNumber + additionalLocalsCount + (isStaticValueType ? 1 : 0);

// Gets the static value type buffer and size
unsigned staticValueTypeTypeRefBuffer;
auto staticValueTypeTypeRefSize = CorSigCompressToken(caller->type.id, &staticValueTypeTypeRefBuffer);

// Gets the calltarget state type buffer and size
unsigned callTargetStateTypeRefBuffer;
Expand Down Expand Up @@ -496,6 +506,13 @@ HRESULT CallTargetTokens::ModifyLocalSig(ILRewriter* reWriter, TypeSignature* me
newSignature.Append(ELEMENT_TYPE_VALUETYPE);
newSignature.Append(&callTargetReturnBuffer, callTargetReturnSize);
}

// Static method in a ValueType
if (isStaticValueType)
{
newSignature.Append(ELEMENT_TYPE_VALUETYPE);
newSignature.Append(&staticValueTypeTypeRefBuffer, staticValueTypeTypeRefSize);
}

// Add custom locals
AddAdditionalLocals(methodReturnValue, methodTypeArguments, newSignature, isAsyncMethod);
Expand All @@ -519,7 +536,7 @@ HRESULT CallTargetTokens::ModifyLocalSig(ILRewriter* reWriter, TypeSignature* me
*callTargetReturnToken = callTargetReturn;

const auto sizeOfOtherIndexes = additionalLocalIndices.size();
auto indexStart = variableNumber + additionalLocalsCount;
auto indexStart = variableNumber + additionalLocalsCount + (isStaticValueType ? 1 : 0);

if (returnSignatureType != nullptr)
{
Expand All @@ -533,6 +550,15 @@ HRESULT CallTargetTokens::ModifyLocalSig(ILRewriter* reWriter, TypeSignature* me
*exceptionIndex = newLocalsCount - indexStart--;
*callTargetReturnIndex = newLocalsCount - indexStart--;

if (isStaticValueType)
{
*staticValueTypeIndex = newLocalsCount - indexStart--;
}
else
{
*staticValueTypeIndex = static_cast<ULONG>(ULONG_MAX);
}

for (unsigned int i = 0; i < sizeOfOtherIndexes; i++)
{
additionalLocalIndices[i] = newLocalsCount - indexStart--;
Expand Down Expand Up @@ -870,9 +896,9 @@ mdAssemblyRef CallTargetTokens::GetCorLibAssemblyRef()
}

HRESULT CallTargetTokens::ModifyLocalSigAndInitialize(void* rewriterWrapperPtr, TypeSignature* methodReturnType,
std::vector<TypeSignature>* methodTypeArguments,
std::vector<TypeSignature>* methodTypeArguments, FunctionInfo* caller,
ULONG* callTargetStateIndex, ULONG* exceptionIndex,
ULONG* callTargetReturnIndex, ULONG* returnValueIndex,
ULONG* callTargetReturnIndex, ULONG* staticValueTypeIndex, ULONG* returnValueIndex,
mdToken* callTargetStateToken, mdToken* exceptionToken,
mdToken* callTargetReturnToken, ILInstr** firstInstruction,
std::vector<ULONG>& additionalLocalIndices, bool isAsyncMethod)
Expand All @@ -881,8 +907,8 @@ HRESULT CallTargetTokens::ModifyLocalSigAndInitialize(void* rewriterWrapperPtr,

// Modify the Local Var Signature of the method

auto hr = ModifyLocalSig(rewriterWrapper->GetILRewriter(), methodReturnType, methodTypeArguments,
callTargetStateIndex, exceptionIndex, callTargetReturnIndex,
auto hr = ModifyLocalSig(rewriterWrapper->GetILRewriter(), methodReturnType, methodTypeArguments, caller,
callTargetStateIndex, exceptionIndex, callTargetReturnIndex, staticValueTypeIndex,
returnValueIndex, callTargetStateToken, exceptionToken, callTargetReturnToken,
additionalLocalIndices, isAsyncMethod);

Expand Down
8 changes: 4 additions & 4 deletions tracer/src/Datadog.Tracer.Native/calltarget_tokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ class CallTargetTokens
mdMemberRef GetCallTargetReturnValueDefaultMemberRef(mdTypeSpec callTargetReturnTypeSpec);
mdMethodSpec GetCallTargetDefaultValueMethodSpec(const TypeSignature* methodArgument);

HRESULT ModifyLocalSig(ILRewriter* reWriter, TypeSignature* methodReturnValue, std::vector<TypeSignature>* methodTypeArguments,
ULONG* callTargetStateIndex, ULONG* exceptionIndex, ULONG* callTargetReturnIndex, ULONG* returnValueIndex,
HRESULT ModifyLocalSig(ILRewriter* reWriter, TypeSignature* methodReturnValue, std::vector<TypeSignature>* methodTypeArguments, FunctionInfo* caller,
ULONG* callTargetStateIndex, ULONG* exceptionIndex, ULONG* callTargetReturnIndex, ULONG* staticValueTypeIndex, ULONG* returnValueIndex,
mdToken* callTargetStateToken, mdToken* exceptionToken, mdToken* callTargetReturnToken,
std::vector<ULONG>& additionalLocalIndices, bool isAsyncMethod = false);

Expand Down Expand Up @@ -97,8 +97,8 @@ class CallTargetTokens

mdMethodDef GetCallTargetStateSkipMethodBodyMemberRef();

HRESULT ModifyLocalSigAndInitialize(void* rewriterWrapperPtr, TypeSignature* methodReturnType, std::vector<TypeSignature>* methodTypeArguments,
ULONG* callTargetStateIndex, ULONG* exceptionIndex, ULONG* callTargetReturnIndex, ULONG* returnValueIndex,
HRESULT ModifyLocalSigAndInitialize(void* rewriterWrapperPtr, TypeSignature* methodReturnType, std::vector<TypeSignature>* methodTypeArguments, FunctionInfo* caller,
ULONG* callTargetStateIndex, ULONG* exceptionIndex, ULONG* callTargetReturnIndex, ULONG* staticValueTypeIndex, ULONG* returnValueIndex,
mdToken* callTargetStateToken, mdToken* exceptionToken,
mdToken* callTargetReturnToken, ILInstr** firstInstruction, std::vector<ULONG>& additionalLocalIndices, bool
isAsyncMethod = false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2239,6 +2239,7 @@ HRESULT DebuggerMethodRewriter::Rewrite(RejitHandlerModule* moduleHandler,
ULONG callTargetStateIndex = static_cast<ULONG>(ULONG_MAX);
ULONG exceptionIndex = static_cast<ULONG>(ULONG_MAX);
ULONG callTargetReturnIndex = static_cast<ULONG>(ULONG_MAX);
ULONG staticValueTypeIndex = static_cast<ULONG>(ULONG_MAX);
ULONG returnValueIndex = static_cast<ULONG>(ULONG_MAX);
mdToken callTargetStateToken = mdTokenNil;
mdToken exceptionToken = mdTokenNil;
Expand Down Expand Up @@ -2275,8 +2276,8 @@ HRESULT DebuggerMethodRewriter::Rewrite(RejitHandlerModule* moduleHandler,
methodReturnType = caller->method_signature.GetReturnValue();
}
auto debuggerLocals = std::vector<ULONG>(debuggerTokens->GetAdditionalLocalsCount(methodArguments));
hr = debuggerTokens->ModifyLocalSigAndInitialize(&rewriterWrapper, &methodReturnType, &methodArguments, &callTargetStateIndex, &exceptionIndex,
&callTargetReturnIndex, &returnValueIndex, &callTargetStateToken,
hr = debuggerTokens->ModifyLocalSigAndInitialize(&rewriterWrapper, &methodReturnType, &methodArguments, caller, &callTargetStateIndex, &exceptionIndex,
&callTargetReturnIndex, &staticValueTypeIndex, &returnValueIndex, &callTargetStateToken,
&exceptionToken, &callTargetReturnToken, &firstInstruction, debuggerLocals, isAsyncMethod);

ULONG lineProbeCallTargetStateIndex = debuggerLocals[0];
Expand Down
98 changes: 68 additions & 30 deletions tracer/src/Datadog.Tracer.Native/tracer_method_rewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,12 @@ HRESULT TracerMethodRewriter::Rewrite(RejitHandlerModule* moduleHandler, RejitHa
Current CallTarget Limitations:
===============================

1. Static methods in a ValueType (struct) cannot be instrumented.
2. Generic ValueTypes (struct) cannot be instrumented.
3. Nested ValueTypes (struct) inside a Generic parent type will not expose the type instance (the instance will
1. Generic ValueTypes (struct) cannot be instrumented.
2. Nested ValueTypes (struct) inside a Generic parent type will not expose the type instance (the instance will
be always null).
4. Nested types (reference types) inside a Generic parent type will not expose the type instance (the instance
3. Nested types (reference types) inside a Generic parent type will not expose the type instance (the instance
will be casted as an object type).
5. Methods in a Generic type will not expose the Generic type instance (the instance will be casted as a non
4. Methods in a Generic type will not expose the Generic type instance (the instance will be casted as a non
generic base type or object type).
*/

Expand Down Expand Up @@ -183,6 +182,7 @@ HRESULT TracerMethodRewriter::Rewrite(RejitHandlerModule* moduleHandler, RejitHa
ULONG callTargetStateIndex = static_cast<ULONG>(ULONG_MAX);
ULONG exceptionIndex = static_cast<ULONG>(ULONG_MAX);
ULONG callTargetReturnIndex = static_cast<ULONG>(ULONG_MAX);
ULONG staticValueTypeIndex = static_cast<ULONG>(ULONG_MAX);
ULONG returnValueIndex = static_cast<ULONG>(ULONG_MAX);

std::vector<ULONG> indexes(tracerTokens->GetAdditionalLocalsCount(methodArguments));
Expand All @@ -193,7 +193,7 @@ HRESULT TracerMethodRewriter::Rewrite(RejitHandlerModule* moduleHandler, RejitHa
auto returnType = caller->method_signature.GetReturnValue();

tracerTokens->ModifyLocalSigAndInitialize(
&reWriterWrapper, &returnType, &methodArguments, &callTargetStateIndex, &exceptionIndex, &callTargetReturnIndex,
&reWriterWrapper, &returnType, &methodArguments, caller, &callTargetStateIndex, &exceptionIndex, &callTargetReturnIndex, &staticValueTypeIndex,
&returnValueIndex, &callTargetStateToken, &exceptionToken, &callTargetReturnToken, &firstInstruction, indexes);

ULONG exceptionValueIndex = indexes[0];
Expand All @@ -207,19 +207,39 @@ HRESULT TracerMethodRewriter::Rewrite(RejitHandlerModule* moduleHandler, RejitHa
// *** Load instance into the stack (if not static)
if (isStatic)
{
if (caller->type.valueType)
bool callerTypeIsValueType = caller->type.valueType;
mdToken callerTypeToken = tracerTokens->GetCurrentTypeRef(&caller->type, callerTypeIsValueType);
if (callerTypeIsValueType && callerTypeToken != mdTokenNil)
{
reWriterWrapper.LoadLocalAddress(staticValueTypeIndex);
if (caller->type.type_spec != mdTypeSpecNil)
{
reWriterWrapper.InitObj(caller->type.type_spec);
}
else if (!caller->type.isGeneric)
{
reWriterWrapper.InitObj(caller->type.id);
}
else
{
// Generic struct instrumentation is not supported
// IMetaDataImport::GetMemberProps and IMetaDataImport::GetMemberRefProps returns
// The parent token as mdTypeDef and not as a mdTypeSpec
// that's because the method definition is stored in the mdTypeDef
// The problem is that we don't have the exact Spec of that generic
// We can't emit LoadObj or Box because that would result in an invalid IL.
// This problem doesn't occur on a class type because we can always relay in the
// object type.
Logger::Warn("*** CallTarget_RewriterCallback(): Generic struct (struct TypeName<T>) instrumentation is not supported.");
return S_FALSE;
}

reWriterWrapper.LoadLocal(staticValueTypeIndex);
}
else
{
// Static methods in a ValueType can't be instrumented.
// In the future this can be supported by adding a local for the valuetype and initialize it to the default
// value. After the signature modification we need to emit the following IL to initialize and load into the
// stack.
// ldloca.s [localIndex]
// initobj [valueType]
// ldloc.s [localIndex]
Logger::Warn("*** CallTarget_RewriterCallback(): Static methods in a ValueType cannot be instrumented. ");
return S_FALSE;
reWriterWrapper.LoadNull();
}
reWriterWrapper.LoadNull();
}
else
{
Expand Down Expand Up @@ -505,21 +525,39 @@ HRESULT TracerMethodRewriter::Rewrite(RejitHandlerModule* moduleHandler, RejitHa
// *** Load instance into the stack (if not static)
if (isStatic)
{
if (caller->type.valueType)
bool callerTypeIsValueType = caller->type.valueType;
mdToken callerTypeToken = tracerTokens->GetCurrentTypeRef(&caller->type, callerTypeIsValueType);
if (caller->type.valueType && callerTypeToken != mdTokenNil)
{
endMethodTryStartInstr = reWriterWrapper.LoadLocalAddress(staticValueTypeIndex);
if (caller->type.type_spec != mdTypeSpecNil)
{
reWriterWrapper.InitObj(caller->type.type_spec);
}
else if (!caller->type.isGeneric)
{
reWriterWrapper.InitObj(caller->type.id);
}
else
{
// Generic struct instrumentation is not supported
// IMetaDataImport::GetMemberProps and IMetaDataImport::GetMemberRefProps returns
// The parent token as mdTypeDef and not as a mdTypeSpec
// that's because the method definition is stored in the mdTypeDef
// The problem is that we don't have the exact Spec of that generic
// We can't emit LoadObj or Box because that would result in an invalid IL.
// This problem doesn't occur on a class type because we can always relay in the
// object type.
Logger::Warn("*** CallTarget_RewriterCallback(): Generic struct (struct TypeName<T>) instrumentation is not supported.");
return S_FALSE;
}

reWriterWrapper.LoadLocal(staticValueTypeIndex);
}
else
{
// Static methods in a ValueType can't be instrumented.
// In the future this can be supported by adding a local for the valuetype
// and initialize it to the default value. After the signature
// modification we need to emit the following IL to initialize and load
// into the stack.
// ldloca.s [localIndex]
// initobj [valueType]
// ldloc.s [localIndex]
Logger::Warn("CallTarget_RewriterCallback: Static methods in a ValueType cannot "
"be instrumented. ");
return S_FALSE;
endMethodTryStartInstr = reWriterWrapper.LoadNull();
}
endMethodTryStartInstr = reWriterWrapper.LoadNull();
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,20 @@ public async Task MethodArgumentsInstrumentation(int numberOfArguments, bool fas
{
// On number of arguments = 0 the throw exception on integrations async continuation runs.
// So we have 1 more case with an exception being reported from the integration.
Assert.Equal(180, beginMethodCount);
Assert.Equal(180, endMethodCount);
Assert.Equal(240, beginMethodCount);
Assert.Equal(240, endMethodCount);
Assert.Equal(44, exceptionCount);
}
else if (numberOfArguments == 1)
{
Assert.Equal(175, beginMethodCount);
Assert.Equal(175, endMethodCount);
Assert.Equal(235, beginMethodCount);
Assert.Equal(235, endMethodCount);
Assert.Equal(40, exceptionCount);
}
else
{
Assert.Equal(168, beginMethodCount);
Assert.Equal(168, endMethodCount);
Assert.Equal(228, beginMethodCount);
Assert.Equal(228, endMethodCount);
Assert.Equal(40, exceptionCount);
}

Expand Down
Loading
Loading