diff --git a/deploy/Datadog.Trace.ClrProfiler.WindowsInstaller/Product.wxs b/deploy/Datadog.Trace.ClrProfiler.WindowsInstaller/Product.wxs
index a1289a807d9d..b65722c95873 100644
--- a/deploy/Datadog.Trace.ClrProfiler.WindowsInstaller/Product.wxs
+++ b/deploy/Datadog.Trace.ClrProfiler.WindowsInstaller/Product.wxs
@@ -76,6 +76,11 @@
Source="$(var.ManagedDllPath)\MsgPack.dll"
KeyPath="yes" Checksum="yes" Assembly=".net"/>
+
+
+
DelayAsync(int seconds)
+ {
+ await Task.Delay(TimeSpan.FromSeconds(seconds)).ConfigureAwait(false);
+ return Json(seconds);
}
[HttpGet]
[Route("api/environment")]
public IHttpActionResult Environment()
{
- return Ok(System.Environment.GetEnvironmentVariables());
+ return Json(System.Environment.GetEnvironmentVariables());
}
}
}
diff --git a/samples/Samples.AspNetMvc5/Samples.AspNetMvc5.csproj b/samples/Samples.AspNetMvc5/Samples.AspNetMvc5.csproj
index ea212a92d85b..52c87085f020 100644
--- a/samples/Samples.AspNetMvc5/Samples.AspNetMvc5.csproj
+++ b/samples/Samples.AspNetMvc5/Samples.AspNetMvc5.csproj
@@ -52,6 +52,9 @@
..\..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll
+
+ ..\..\packages\Microsoft.AspNet.WebApi.Tracing.5.2.3\lib\net45\System.Web.Http.Tracing.dll
+
..\..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll
diff --git a/samples/Samples.AspNetMvc5/packages.config b/samples/Samples.AspNetMvc5/packages.config
index 311e58d94b80..64f0b26cf614 100644
--- a/samples/Samples.AspNetMvc5/packages.config
+++ b/samples/Samples.AspNetMvc5/packages.config
@@ -6,6 +6,7 @@
+
diff --git a/src/Datadog.Trace.ClrProfiler.Managed/Datadog.Trace.ClrProfiler.Managed.csproj b/src/Datadog.Trace.ClrProfiler.Managed/Datadog.Trace.ClrProfiler.Managed.csproj
index 8a7b93ea4d9b..c3c274b299f9 100644
--- a/src/Datadog.Trace.ClrProfiler.Managed/Datadog.Trace.ClrProfiler.Managed.csproj
+++ b/src/Datadog.Trace.ClrProfiler.Managed/Datadog.Trace.ClrProfiler.Managed.csproj
@@ -19,8 +19,8 @@
-
-
+
+
diff --git a/src/Datadog.Trace.ClrProfiler.Managed/DynamicMethodBuilder.cs b/src/Datadog.Trace.ClrProfiler.Managed/DynamicMethodBuilder.cs
index eb1bb8dd805e..93908f2cba2e 100644
--- a/src/Datadog.Trace.ClrProfiler.Managed/DynamicMethodBuilder.cs
+++ b/src/Datadog.Trace.ClrProfiler.Managed/DynamicMethodBuilder.cs
@@ -4,6 +4,7 @@
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
+using Sigil;
namespace Datadog.Trace.ClrProfiler
{
@@ -12,9 +13,9 @@ namespace Datadog.Trace.ClrProfiler
///
/// The type of delegate
public static class DynamicMethodBuilder
- where TDelegate : Delegate
+ where TDelegate : Delegate
{
- private static ConcurrentDictionary _cached = new ConcurrentDictionary(new KeyComparer());
+ private static readonly ConcurrentDictionary _cached = new ConcurrentDictionary(new KeyComparer());
///
/// Memoizes CreateMethodCallDelegate
@@ -31,21 +32,22 @@ public static TDelegate GetOrCreateMethodCallDelegate(
Type[] methodGenericArguments = null)
{
return _cached.GetOrAdd(
- new Key(type, methodName, methodParameterTypes, methodGenericArguments),
- key =>
- {
- return CreateMethodCallDelegate(key.Type, key.MethodName, key.MethodParameterTypes, key.MethodGenericArguments);
- });
+ new Key(type, methodName, methodParameterTypes, methodGenericArguments),
+ key => CreateMethodCallDelegate(
+ key.Type,
+ key.MethodName,
+ key.MethodParameterTypes,
+ key.MethodGenericArguments));
}
///
/// Creates a simple using that
- /// calls a method with the specified name and and parameter types.
+ /// calls a method with the specified name and parameter types.
///
- /// The that contains the method.
- /// The name of the method.
- /// optional types for the method parameters
- /// optional generic type arguments for a generic method
+ /// The that contains the method to call when the returned delegate is executed..
+ /// The name of the method to call when the returned delegate is executed.
+ /// If not null, use method overload that matches the specified parameters.
+ /// If not null, use method overload that has the same number of generic arguments.
/// A that can be used to execute the dynamic method.
public static TDelegate CreateMethodCallDelegate(
Type type,
@@ -76,38 +78,42 @@ public static TDelegate CreateMethodCallDelegate(
}
// find any method that matches by name and parameter types
- var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
- methods = methods.Where(m => m.Name == methodName).ToArray();
+ IEnumerable methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)
+ .Where(m => m.Name == methodName);
+
+ // if methodParameterTypes was specified, check for a method that matches
if (methodParameterTypes != null)
{
methods = methods.Where(m =>
- {
- var ps = m.GetParameters();
- if (ps.Length != methodParameterTypes.Length)
- {
- return false;
- }
+ {
+ var ps = m.GetParameters();
+ if (ps.Length != methodParameterTypes.Length)
+ {
+ return false;
+ }
- for (var i = 0; i < ps.Length; i++)
- {
- var t1 = ps[i].ParameterType;
- var t2 = methodParameterTypes[i];
+ for (var i = 0; i < ps.Length; i++)
+ {
+ var t1 = ps[i].ParameterType;
+ var t2 = methodParameterTypes[i];
- // generics can be tricky to compare for type equality
- // so we will just check the namespace and name
- if (t1.Namespace != t2.Namespace || t1.Name != t2.Name)
- {
- return false;
- }
- }
+ // generics can be tricky to compare for type equality
+ // so we will just check the namespace and name
+ if (t1.Namespace != t2.Namespace || t1.Name != t2.Name)
+ {
+ return false;
+ }
+ }
- return true;
- }).ToArray();
+ return true;
+ });
}
if (methodGenericArguments != null)
{
- methods = methods.Where(m => m.IsGenericMethodDefinition).ToArray();
+ methods = methods.Where(m => m.IsGenericMethodDefinition &&
+ m.GetGenericArguments().Length == methodGenericArguments.Length)
+ .ToArray();
}
MethodInfo methodInfo = methods.FirstOrDefault();
@@ -123,41 +129,53 @@ public static TDelegate CreateMethodCallDelegate(
methodInfo = methodInfo.MakeGenericMethod(methodGenericArguments);
}
- var dynamicMethod = new DynamicMethod(methodName, returnType, parameterTypes, type);
- ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
+ Type[] effectiveParameterTypes;
- for (int argumentIndex = 0; argumentIndex < parameterTypes.Length; argumentIndex++)
+ IEnumerable reflectedParameterTypes = methodInfo.GetParameters()
+ .Select(p => p.ParameterType);
+ if (methodInfo.IsStatic)
{
- if (argumentIndex == 0)
- {
- ilGenerator.Emit(OpCodes.Ldarg_0);
- }
- else if (argumentIndex == 1)
- {
- ilGenerator.Emit(OpCodes.Ldarg_1);
- }
- else if (argumentIndex == 2)
- {
- ilGenerator.Emit(OpCodes.Ldarg_2);
- }
- else if (argumentIndex == 3)
- {
- ilGenerator.Emit(OpCodes.Ldarg_3);
- }
- else if (argumentIndex < 256)
+ effectiveParameterTypes = reflectedParameterTypes.ToArray();
+ }
+ else
+ {
+ // for instance methods, insert object's type as first element in array
+ effectiveParameterTypes = new[] { type }
+ .Concat(reflectedParameterTypes)
+ .ToArray();
+ }
+
+ Emit dynamicMethod = Emit.NewDynamicMethod(methodInfo.Name);
+
+ // load each argument and cast or unbox as necessary
+ for (ushort argumentIndex = 0; argumentIndex < parameterTypes.Length; argumentIndex++)
+ {
+ Type delegateParameterType = parameterTypes[argumentIndex];
+ Type underlyingParameterType = effectiveParameterTypes[argumentIndex];
+
+ dynamicMethod.LoadArgument(argumentIndex);
+
+ if (underlyingParameterType.IsValueType && delegateParameterType == typeof(object))
{
- ilGenerator.Emit(OpCodes.Ldarg_S, (byte)argumentIndex);
+ dynamicMethod.UnboxAny(underlyingParameterType);
}
- else
+ else if (underlyingParameterType != delegateParameterType)
{
- ilGenerator.Emit(OpCodes.Ldarg, argumentIndex);
+ dynamicMethod.CastClass(underlyingParameterType);
}
}
- ilGenerator.Emit(methodInfo.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, methodInfo);
- ilGenerator.Emit(OpCodes.Ret);
+ if (methodInfo.IsVirtual)
+ {
+ dynamicMethod.CallVirtual(methodInfo);
+ }
+ else
+ {
+ dynamicMethod.Call(methodInfo);
+ }
- return (TDelegate)dynamicMethod.CreateDelegate(delegateType);
+ dynamicMethod.Return();
+ return dynamicMethod.CreateDelegate();
}
private struct Key
diff --git a/src/Datadog.Trace.ClrProfiler.Managed/Integrations/AspNetWebApi2Integration.cs b/src/Datadog.Trace.ClrProfiler.Managed/Integrations/AspNetWebApi2Integration.cs
index 62b324a656f8..a6b240711e5b 100644
--- a/src/Datadog.Trace.ClrProfiler.Managed/Integrations/AspNetWebApi2Integration.cs
+++ b/src/Datadog.Trace.ClrProfiler.Managed/Integrations/AspNetWebApi2Integration.cs
@@ -1,3 +1,6 @@
+#if NET45
+
+using System;
using System.Collections.Generic;
using System.Threading;
using Datadog.Trace.ExtensionMethods;
@@ -11,6 +14,8 @@ public static class AspNetWebApi2Integration
{
internal const string OperationName = "aspnet-web-api.request";
+ private static readonly Type HttpControllerContextType = Type.GetType("System.Web.Http.Controllers.HttpControllerContext, System.Web.Http", throwOnError: false);
+
///
/// ExecuteAsync calls the underlying ExecuteAsync and traces the request.
///
@@ -18,23 +23,37 @@ public static class AspNetWebApi2Integration
/// The controller context for the call
/// The cancellation token source
/// A task with the result
- public static dynamic ExecuteAsync(dynamic @this, dynamic controllerContext, dynamic cancellationTokenSource)
+ public static object ExecuteAsync(object @this, object controllerContext, object cancellationTokenSource)
{
+ Type controllerType = @this.GetType();
+
+ // in some cases, ExecuteAsync() is an explicit interface implementation,
+ // which is not public and has a different name, so try both
+ var executeAsyncFunc =
+ DynamicMethodBuilder>
+ .GetOrCreateMethodCallDelegate(controllerType, "ExecuteAsync") ??
+ DynamicMethodBuilder>
+ .GetOrCreateMethodCallDelegate(controllerType, "System.Web.Http.Controllers.IHttpController.ExecuteAsync");
+
using (Scope scope = CreateScope(controllerContext))
{
return scope.Span.Trace(
- () => @this.ExecuteAsync(controllerContext, ((CancellationTokenSource)cancellationTokenSource).Token),
- onComplete: e =>
- {
- if (e != null)
- {
- scope.Span.SetException(e);
- }
+ () =>
+ {
+ CancellationToken cancellationToken = ((CancellationTokenSource)cancellationTokenSource).Token;
+ return executeAsyncFunc(@this, controllerContext, cancellationToken);
+ },
+ onComplete: e =>
+ {
+ if (e != null)
+ {
+ scope.Span.SetException(e);
+ }
- // some fields aren't set till after execution, so repopulate anything missing
- UpdateSpan(controllerContext, scope.Span);
- scope.Span.Finish();
- });
+ // some fields aren't set till after execution, so repopulate anything missing
+ UpdateSpan(controllerContext, scope.Span);
+ scope.Span.Finish();
+ });
}
}
@@ -92,3 +111,5 @@ private static void UpdateSpan(dynamic controllerContext, Span span)
}
}
}
+
+#endif
diff --git a/src/Datadog.Trace.ClrProfiler.Managed/Integrations/StackExchange.Redis/ConnectionMultiplexer.cs b/src/Datadog.Trace.ClrProfiler.Managed/Integrations/StackExchange.Redis/ConnectionMultiplexer.cs
index 698cdfb76d4d..b34944ae4537 100644
--- a/src/Datadog.Trace.ClrProfiler.Managed/Integrations/StackExchange.Redis/ConnectionMultiplexer.cs
+++ b/src/Datadog.Trace.ClrProfiler.Managed/Integrations/StackExchange.Redis/ConnectionMultiplexer.cs
@@ -1,6 +1,4 @@
using System;
-using System.Collections.Concurrent;
-using System.Linq;
using System.Threading.Tasks;
namespace Datadog.Trace.ClrProfiler.Integrations.StackExchange.Redis
@@ -52,11 +50,11 @@ public static T ExecuteSyncImpl(object multiplexer, object message, object pr
/// An asynchronous task.
public static object ExecuteAsyncImpl(object multiplexer, object message, object processor, object state, object server)
{
- var resultType = typeof(Task);
+ var genericType = typeof(T);
var asm = multiplexer.GetType().Assembly;
var multiplexerType = asm.GetType("StackExchange.Redis.ConnectionMultiplexer");
var messageType = asm.GetType("StackExchange.Redis.Message");
- var processorType = asm.GetType("StackExchange.Redis.ResultProcessor`1").MakeGenericType(resultType);
+ var processorType = asm.GetType("StackExchange.Redis.ResultProcessor`1").MakeGenericType(genericType);
var stateType = typeof(object);
var serverType = asm.GetType("StackExchange.Redis.ServerEndPoint");
@@ -64,7 +62,7 @@ public static object ExecuteAsyncImpl(object multiplexer, object message, obj
multiplexerType,
"ExecuteAsyncImpl",
new Type[] { messageType, processorType, stateType, serverType },
- new Type[] { resultType });
+ new Type[] { genericType });
using (var scope = CreateScope(multiplexer, message, server, finishOnClose: false))
{
diff --git a/src/Datadog.Trace.ClrProfiler.Managed/Integrations/StackExchange.Redis/RedisBatch.cs b/src/Datadog.Trace.ClrProfiler.Managed/Integrations/StackExchange.Redis/RedisBatch.cs
index 5ae2bbf75f34..6cd75bd849a6 100644
--- a/src/Datadog.Trace.ClrProfiler.Managed/Integrations/StackExchange.Redis/RedisBatch.cs
+++ b/src/Datadog.Trace.ClrProfiler.Managed/Integrations/StackExchange.Redis/RedisBatch.cs
@@ -1,7 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Threading.Tasks;
namespace Datadog.Trace.ClrProfiler.Integrations.StackExchange.Redis
@@ -22,18 +19,18 @@ public class RedisBatch : Base
/// An asynchronous task.
public static object ExecuteAsync(object obj, object message, object processor, object server)
{
- var resultType = typeof(Task);
+ var genericType = typeof(T);
var asm = obj.GetType().Assembly;
var batchType = asm.GetType("StackExchange.Redis.RedisBatch");
var messageType = asm.GetType("StackExchange.Redis.Message");
- var processorType = asm.GetType("StackExchange.Redis.ResultProcessor`1").MakeGenericType(typeof(T));
+ var processorType = asm.GetType("StackExchange.Redis.ResultProcessor`1").MakeGenericType(genericType);
var serverType = asm.GetType("StackExchange.Redis.ServerEndPoint");
var originalMethod = DynamicMethodBuilder>>.CreateMethodCallDelegate(
obj.GetType(),
"ExecuteAsync",
new Type[] { messageType, processorType, serverType },
- new Type[] { resultType });
+ new Type[] { genericType });
// we only trace RedisBatch methods here
if (obj.GetType() == batchType)
diff --git a/src/Datadog.Trace.ClrProfiler.Native/util.cpp b/src/Datadog.Trace.ClrProfiler.Native/util.cpp
index f6d325a40e98..6aa5dd07510d 100644
--- a/src/Datadog.Trace.ClrProfiler.Native/util.cpp
+++ b/src/Datadog.Trace.ClrProfiler.Native/util.cpp
@@ -36,13 +36,13 @@ WSTRING Trim(const WSTRING &str) {
WSTRING trimmed = str;
auto lpos = trimmed.find_first_not_of(" \t"_W);
- if (lpos != WSTRING::npos) {
+ if (lpos != WSTRING::npos && lpos > 0) {
trimmed = trimmed.substr(lpos);
}
- auto rpos = trimmed.find_last_of(" \t"_W);
+ auto rpos = trimmed.find_last_not_of(" \t"_W);
if (rpos != WSTRING::npos) {
- trimmed = trimmed.substr(0, rpos);
+ trimmed = trimmed.substr(0, rpos + 1);
}
return trimmed;