Convert AdoNet to use mdToken (#438)
* Convert AdoNet to use mdToken * Use DbCommand as concrete type
This commit is contained in:
parent
e8dd7232b1
commit
dbd1945354
|
|
@ -17,7 +17,7 @@ namespace Datadog.Trace.ClrProfiler.Emit
|
|||
private static readonly ConcurrentDictionary<Key, TDelegate> Cache = new ConcurrentDictionary<Key, TDelegate>(new KeyComparer());
|
||||
private static readonly ILog Log = LogProvider.GetLogger(typeof(MethodBuilder<TDelegate>));
|
||||
|
||||
private readonly Assembly _callingAssembly;
|
||||
private readonly Assembly _resolutionAssembly;
|
||||
private readonly int _mdToken;
|
||||
private readonly int _originalOpCodeValue;
|
||||
private readonly OpCodeValue _opCode;
|
||||
|
|
@ -36,9 +36,9 @@ namespace Datadog.Trace.ClrProfiler.Emit
|
|||
private Type[] _methodGenerics;
|
||||
private bool _forceMethodDefResolve;
|
||||
|
||||
private MethodBuilder(Assembly callingAssembly, int mdToken, int opCode, string methodName)
|
||||
private MethodBuilder(Assembly resolutionAssembly, int mdToken, int opCode, string methodName)
|
||||
{
|
||||
_callingAssembly = callingAssembly;
|
||||
_resolutionAssembly = resolutionAssembly;
|
||||
_mdToken = mdToken;
|
||||
_opCode = (OpCodeValue)opCode;
|
||||
_originalOpCodeValue = opCode;
|
||||
|
|
@ -46,9 +46,9 @@ namespace Datadog.Trace.ClrProfiler.Emit
|
|||
_forceMethodDefResolve = false;
|
||||
}
|
||||
|
||||
public static MethodBuilder<TDelegate> Start(Assembly callingAssembly, int mdToken, int opCode, string methodName)
|
||||
public static MethodBuilder<TDelegate> Start(Assembly resolutionAssembly, int mdToken, int opCode, string methodName)
|
||||
{
|
||||
return new MethodBuilder<TDelegate>(callingAssembly, mdToken, opCode, methodName);
|
||||
return new MethodBuilder<TDelegate>(resolutionAssembly, mdToken, opCode, methodName);
|
||||
}
|
||||
|
||||
public MethodBuilder<TDelegate> WithConcreteType(Type type)
|
||||
|
|
@ -60,7 +60,7 @@ namespace Datadog.Trace.ClrProfiler.Emit
|
|||
|
||||
public MethodBuilder<TDelegate> WithConcreteTypeName(string typeName)
|
||||
{
|
||||
var concreteType = _callingAssembly.GetType(typeName);
|
||||
var concreteType = _resolutionAssembly.GetType(typeName);
|
||||
return this.WithConcreteType(concreteType);
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ namespace Datadog.Trace.ClrProfiler.Emit
|
|||
public TDelegate Build()
|
||||
{
|
||||
var cacheKey = new Key(
|
||||
callingModule: _callingAssembly.ManifestModule,
|
||||
callingModule: _resolutionAssembly.ManifestModule,
|
||||
mdToken: _mdToken,
|
||||
callOpCode: _opCode,
|
||||
concreteType: _concreteType,
|
||||
|
|
@ -136,12 +136,12 @@ namespace Datadog.Trace.ClrProfiler.Emit
|
|||
if (_forceMethodDefResolve || (_declaringTypeGenerics == null && _methodGenerics == null))
|
||||
{
|
||||
_methodBase =
|
||||
_callingAssembly.ManifestModule.ResolveMethod(metadataToken: _mdToken);
|
||||
_resolutionAssembly.ManifestModule.ResolveMethod(metadataToken: _mdToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
_methodBase =
|
||||
_callingAssembly.ManifestModule.ResolveMethod(
|
||||
_resolutionAssembly.ManifestModule.ResolveMethod(
|
||||
metadataToken: _mdToken,
|
||||
genericTypeArguments: _declaringTypeGenerics,
|
||||
genericMethodArguments: _methodGenerics);
|
||||
|
|
@ -281,7 +281,7 @@ namespace Datadog.Trace.ClrProfiler.Emit
|
|||
private MethodInfo VerifyMethodFromToken(MethodInfo methodInfo)
|
||||
{
|
||||
// Verify baselines to ensure this isn't the wrong method somehow
|
||||
var detailMessage = $"Unexpected method: {_concreteTypeName}.{_methodName} received for mdToken: {_mdToken} in assembly: {_callingAssembly.FullName}";
|
||||
var detailMessage = $"Unexpected method: {_concreteTypeName}.{_methodName} received for mdToken: {_mdToken} in assembly: {_resolutionAssembly.FullName}";
|
||||
|
||||
if (!string.Equals(_methodName, methodInfo.Name))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Datadog.Trace.ClrProfiler.Emit;
|
||||
using Datadog.Trace.ClrProfiler.Helpers;
|
||||
using Datadog.Trace.ExtensionMethods;
|
||||
using Datadog.Trace.Logging;
|
||||
|
||||
|
|
@ -16,6 +18,11 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
{
|
||||
private const string IntegrationName = "AdoNet";
|
||||
private const string Major4 = "4";
|
||||
private const string FrameworkAssembly = "System.Data";
|
||||
private const string CoreAssembly = "System.Data.Common";
|
||||
private const string DbCommand = "System.Data.Common.DbCommand";
|
||||
private const string DbDataReader = "System.Data.Common.DbDataReader";
|
||||
private const string CommandBehavior = "System.Data.CommandBehavior";
|
||||
|
||||
private static readonly ILog Log = LogProvider.GetLogger(typeof(AdoNetIntegration));
|
||||
|
||||
|
|
@ -28,37 +35,49 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
/// <param name="mdToken">The mdToken of the original method call.</param>
|
||||
/// <returns>The value returned by the instrumented method.</returns>
|
||||
[InterceptMethod(
|
||||
TargetAssembly = "System.Data", // .NET Framework
|
||||
TargetType = "System.Data.Common.DbCommand",
|
||||
TargetSignatureTypes = new[] { "System.Data.Common.DbDataReader", "System.Data.CommandBehavior" },
|
||||
TargetAssembly = FrameworkAssembly,
|
||||
TargetType = DbCommand,
|
||||
TargetSignatureTypes = new[] { DbDataReader, CommandBehavior },
|
||||
TargetMinimumVersion = Major4,
|
||||
TargetMaximumVersion = Major4)]
|
||||
[InterceptMethod(
|
||||
TargetAssembly = "System.Data.Common", // .NET Core
|
||||
TargetType = "System.Data.Common.DbCommand",
|
||||
TargetSignatureTypes = new[] { "System.Data.Common.DbDataReader", "System.Data.CommandBehavior" },
|
||||
TargetAssembly = CoreAssembly,
|
||||
TargetType = DbCommand,
|
||||
TargetSignatureTypes = new[] { DbDataReader, CommandBehavior },
|
||||
TargetMinimumVersion = Major4,
|
||||
TargetMaximumVersion = Major4)]
|
||||
public static object ExecuteDbDataReader(object @this, int behavior, int opCode, int mdToken)
|
||||
{
|
||||
var command = (DbCommand)@this;
|
||||
var commandBehavior = (CommandBehavior)behavior;
|
||||
var instanceType = command.GetType();
|
||||
|
||||
var executeReader = Emit.DynamicMethodBuilder<Func<object, CommandBehavior, object>>
|
||||
.GetOrCreateMethodCallDelegate(
|
||||
typeof(DbCommand),
|
||||
"ExecuteDbDataReader",
|
||||
(OpCodeValue)opCode);
|
||||
Func<object, CommandBehavior, object> instrumentedMethod = null;
|
||||
|
||||
try
|
||||
{
|
||||
instrumentedMethod =
|
||||
MethodBuilder<Func<object, CommandBehavior, object>>
|
||||
.Start(instanceType.Assembly, mdToken, opCode, nameof(ExecuteDbDataReader))
|
||||
.WithConcreteType(typeof(DbCommand))
|
||||
.WithParameters(commandBehavior)
|
||||
.Build();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ErrorException($"Error resolving {DbCommand}.{nameof(ExecuteDbDataReader)}(...)", ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
using (var scope = CreateScope(command))
|
||||
{
|
||||
try
|
||||
{
|
||||
return executeReader(command, commandBehavior);
|
||||
return instrumentedMethod(command, commandBehavior);
|
||||
}
|
||||
catch (Exception ex) when (scope?.Span.SetExceptionForFilter(ex) ?? false)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// unreachable code
|
||||
scope?.Span.SetException(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
@ -74,50 +93,77 @@ namespace Datadog.Trace.ClrProfiler.Integrations
|
|||
/// <param name="mdToken">The mdToken of the original method call.</param>
|
||||
/// <returns>The value returned by the instrumented method.</returns>
|
||||
[InterceptMethod(
|
||||
TargetAssembly = "System.Data", // .NET Framework
|
||||
TargetType = "System.Data.Common.DbCommand",
|
||||
TargetSignatureTypes = new[] { "System.Threading.Tasks.Task`1<System.Data.Common.DbDataReader>", "System.Data.CommandBehavior", "System.Threading.CancellationToken" },
|
||||
TargetAssembly = FrameworkAssembly,
|
||||
TargetType = DbCommand,
|
||||
TargetSignatureTypes = new[] { "System.Threading.Tasks.Task`1<System.Data.Common.DbDataReader>", CommandBehavior, ClrNames.CancellationToken },
|
||||
TargetMinimumVersion = Major4,
|
||||
TargetMaximumVersion = Major4)]
|
||||
[InterceptMethod(
|
||||
TargetAssembly = "System.Data.Common", // .NET Core
|
||||
TargetType = "System.Data.Common.DbCommand",
|
||||
TargetSignatureTypes = new[] { "System.Threading.Tasks.Task`1<System.Data.Common.DbDataReader>", "System.Data.CommandBehavior", "System.Threading.CancellationToken" },
|
||||
TargetAssembly = CoreAssembly,
|
||||
TargetType = DbCommand,
|
||||
TargetSignatureTypes = new[] { "System.Threading.Tasks.Task`1<System.Data.Common.DbDataReader>", CommandBehavior, ClrNames.CancellationToken },
|
||||
TargetMinimumVersion = Major4,
|
||||
TargetMaximumVersion = Major4)]
|
||||
public static object ExecuteDbDataReaderAsync(object @this, int behavior, object cancellationTokenSource, int opCode, int mdToken)
|
||||
public static object ExecuteDbDataReaderAsync(
|
||||
object @this,
|
||||
int behavior,
|
||||
object cancellationTokenSource,
|
||||
int opCode,
|
||||
int mdToken)
|
||||
{
|
||||
var tokenSource = cancellationTokenSource as CancellationTokenSource;
|
||||
var cancellationToken = tokenSource?.Token ?? CancellationToken.None;
|
||||
|
||||
var command = (DbCommand)@this;
|
||||
var instanceType = command.GetType();
|
||||
var commandBehavior = (CommandBehavior)behavior;
|
||||
var callOpCode = (OpCodeValue)opCode;
|
||||
var callingAssembly = Assembly.GetCallingAssembly();
|
||||
var dataReaderType = callingAssembly.GetType(DbDataReader);
|
||||
|
||||
return ExecuteDbDataReaderAsyncInternal(command, commandBehavior, cancellationToken, callOpCode);
|
||||
Func<object, object, object, object> instrumentedMethod = null;
|
||||
|
||||
try
|
||||
{
|
||||
instrumentedMethod =
|
||||
MethodBuilder<Func<object, object, object, object>>
|
||||
.Start(Assembly.GetCallingAssembly(), mdToken, opCode, nameof(ExecuteDbDataReaderAsync))
|
||||
.WithConcreteType(typeof(DbCommand))
|
||||
.WithParameters(commandBehavior, cancellationToken)
|
||||
.Build();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ErrorException($"Error resolving {DbCommand}.{nameof(ExecuteDbDataReaderAsync)}(...)", ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
return AsyncHelper.InvokeGenericTaskDelegate(
|
||||
instanceType,
|
||||
dataReaderType,
|
||||
nameof(ExecuteDbDataReaderAsyncInternal),
|
||||
typeof(AdoNetIntegration),
|
||||
command,
|
||||
commandBehavior,
|
||||
cancellationToken,
|
||||
instrumentedMethod);
|
||||
}
|
||||
|
||||
private static async Task<object> ExecuteDbDataReaderAsyncInternal(
|
||||
private static async Task<T> ExecuteDbDataReaderAsyncInternal<T>(
|
||||
DbCommand command,
|
||||
CommandBehavior behavior,
|
||||
CancellationToken cancellationToken,
|
||||
OpCodeValue callOpCode)
|
||||
Func<object, object, object, object> originalMethod)
|
||||
{
|
||||
var executeReader = Emit.DynamicMethodBuilder<Func<object, CommandBehavior, CancellationToken, Task<object>>>
|
||||
.GetOrCreateMethodCallDelegate(
|
||||
typeof(DbCommand),
|
||||
"ExecuteDbDataReaderAsync",
|
||||
callOpCode);
|
||||
|
||||
using (var scope = CreateScope(command))
|
||||
{
|
||||
try
|
||||
{
|
||||
return await executeReader(command, behavior, cancellationToken).ConfigureAwait(false);
|
||||
var awaitable = (Task<T>)originalMethod(command, behavior, cancellationToken);
|
||||
return await awaitable.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) when (scope?.Span.SetExceptionForFilter(ex) ?? false)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// unreachable code
|
||||
scope?.Span.SetException(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue