Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions tracer/build/supported_calltargets.g.json
Original file line number Diff line number Diff line change
Expand Up @@ -1569,7 +1569,7 @@
"InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ControllerActionInvoker_InvokeAction_Integration",
"IntegrationKind": 0,
"IsAdoNetIntegration": false,
"InstrumentationCategory": 6
"InstrumentationCategory": 7
},
{
"IntegrationName": "AspNetWebApi2",
Expand Down Expand Up @@ -1620,7 +1620,7 @@
"InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ReflectedHttpActionDescriptor_ExecuteAsync_Integration",
"IntegrationKind": 0,
"IsAdoNetIntegration": false,
"InstrumentationCategory": 6
"InstrumentationCategory": 7
},
{
"IntegrationName": "AspNetWebApi2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// <copyright file="ActionDescriptorWithMethodInfo.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
#nullable enable

using System.Reflection;
using Datadog.Trace.DuckTyping;

namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet
{
/// <summary>
/// Duck type for MVC/WebApi2 descriptors that expose a MethodInfo.
/// </summary>
[DuckCopy]
internal struct ActionDescriptorWithMethodInfo
{
[Duck]
public MethodInfo MethodInfo;
}
}
#endif

Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Web;
using Datadog.Trace.AppSec;
using Datadog.Trace.AppSec.Coordinator;
using Datadog.Trace.AspNet;
using Datadog.Trace.ClrProfiler.CallTarget;
using Datadog.Trace.Configuration;
using Datadog.Trace.Debugger;
using Datadog.Trace.Debugger.SpanCodeOrigin;
using Datadog.Trace.DuckTyping;
using Datadog.Trace.Logging;
using Datadog.Trace.Vendors.Serilog.Events;

namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet
{
Expand All @@ -31,7 +35,7 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet
MinimumVersion = "4",
MaximumVersion = "5",
IntegrationName = IntegrationName,
InstrumentationCategory = InstrumentationCategory.AppSec | InstrumentationCategory.Iast)]
InstrumentationCategory = InstrumentationCategory.Tracing | InstrumentationCategory.AppSec | InstrumentationCategory.Iast)]
// ReSharper disable once InconsistentNaming
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
Expand Down Expand Up @@ -70,9 +74,62 @@ internal static CallTargetState OnMethodBegin<TTarget, TContext, TActionDescript
Log.Error(ex, "Error instrumenting method {MethodName}", "System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod()");
}

try
{
var codeOrigin = DebuggerManager.Instance.CodeOrigin;
if (codeOrigin is { Settings.CodeOriginForSpansEnabled: true })
{
AddSpanCodeOrigin(actionDescriptor, codeOrigin);
}
}
catch (Exception ex)
{
Log.Error(ex, "Error adding code origin for spans in {MethodName}", "System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod()");
}

return CallTargetState.GetDefault();
}

private static void AddSpanCodeOrigin<TActionDescriptor>(TActionDescriptor actionDescriptor, SpanCodeOrigin codeOrigin)
{
if (actionDescriptor is null)
{
return;
}

var httpContext = HttpContext.Current;
if (SharedItems.TryPeekScope(httpContext, AspNetMvcIntegration.HttpContextKey) is not { Root.Span: { } rootSpan })
{
if (Log.IsEnabled(LogEventLevel.Debug))
{
Log.Debug(
"Code origin is enabled but scope was not found in HttpContext (key: {HttpContextKey}, httpContextNull: {HttpContextIsNull}, itemsCount: {HttpContextItemsCount}, actionDescriptorType: {ActionDescriptorType}).",
AspNetMvcIntegration.HttpContextKey,
httpContext is null,
httpContext?.Items?.Count ?? 0,
actionDescriptor.GetType());
}

return;
}

if (!actionDescriptor.TryDuckCast<ActionDescriptorWithMethodInfo>(out var reflected)
|| reflected.MethodInfo is not { } actionMethod
|| actionMethod.DeclaringType is not { } actionType)
{
if (Log.IsEnabled(LogEventLevel.Debug))
{
Log.Debug(
"Code origin is enabled but could not extract action from ActionDescriptor type {ActionDescriptorType} or action MethodInfo has no DeclaringType.",
actionDescriptor.GetType());
}

return;
}

codeOrigin.SetCodeOriginForEntrySpan(rootSpan, actionType, actionMethod);
}

/// <summary>
/// OnMethodEnd callback
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
using Datadog.Trace.AspNet;
using Datadog.Trace.ClrProfiler.CallTarget;
using Datadog.Trace.Configuration;
using Datadog.Trace.Debugger;
using Datadog.Trace.Debugger.SpanCodeOrigin;
using Datadog.Trace.DuckTyping;
using Datadog.Trace.Logging;
using Datadog.Trace.Vendors.Serilog.Events;

namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet
{
Expand All @@ -33,7 +36,7 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet
MinimumVersion = "5.1",
MaximumVersion = "5",
IntegrationName = IntegrationName,
InstrumentationCategory = InstrumentationCategory.AppSec | InstrumentationCategory.Iast)]
InstrumentationCategory = InstrumentationCategory.Tracing | InstrumentationCategory.AppSec | InstrumentationCategory.Iast)]
// ReSharper disable once InconsistentNaming
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
Expand Down Expand Up @@ -70,9 +73,62 @@ internal static CallTargetState OnMethodBegin<TTarget, TController>(TTarget inst
Log.Error(ex, "Error instrumenting method {MethodName}", "System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync()");
}

try
{
var codeOrigin = DebuggerManager.Instance.CodeOrigin;
if (codeOrigin is { Settings.CodeOriginForSpansEnabled: true })
{
AddSpanCodeOrigin(instance, codeOrigin);
}
}
catch (Exception ex) when (BlockException.GetBlockException(ex) is null)
{
Log.Error(ex, "Error adding code origin for spans in {MethodName}", "System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync()");
}

return CallTargetState.GetDefault();
}

private static void AddSpanCodeOrigin<TTarget>(TTarget instance, SpanCodeOrigin codeOrigin)
{
if (instance == null)
{
return;
}

var httpContext = HttpContext.Current;
if (SharedItems.TryPeekScope(httpContext, AspNetWebApi2Integration.HttpContextKey) is not { Root.Span: { } rootSpan })
{
if (Log.IsEnabled(LogEventLevel.Debug))
{
Log.Debug(
"Code origin is enabled but scope was not found in HttpContext (key: {HttpContextKey}, httpContextNull: {HttpContextIsNull}, itemsCount: {HttpContextItemsCount}, actionDescriptorType: {ActionDescriptorType}).",
AspNetWebApi2Integration.HttpContextKey,
httpContext is null,
httpContext?.Items?.Count ?? 0,
instance.GetType());
}

return;
}

if (!instance.TryDuckCast<ActionDescriptorWithMethodInfo>(out var reflected)
|| reflected.MethodInfo is not { } actionMethod
|| actionMethod.DeclaringType is not { } actionType)
{
if (Log.IsEnabled(LogEventLevel.Debug))
{
Log.Debug(
"Code origin is enabled but could not extract action from HttpActionDescriptor type {ActionDescriptorType} or action has no DeclaringType.",
instance.GetType());
}

return;
}

codeOrigin.SetCodeOriginForEntrySpan(rootSpan, actionType, actionMethod);
}

internal static TResponse? OnAsyncMethodEnd<TTarget, TResponse>(TTarget instance, TResponse? response, Exception? exception, in CallTargetState state)
{
var security = Security.Instance;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="EndpointDetector.cs" company="Datadog">
// <copyright file="EndpointDetector.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>
Expand All @@ -18,20 +18,20 @@ namespace Datadog.Trace.Debugger.SpanCodeOrigin;

internal static class EndpointDetector
{
private static readonly HashSet<string> ControllerAttributes =
private static readonly HashSet<string> AspNetCoreControllerAttributes =
[
"Microsoft.AspNetCore.Mvc.ApiControllerAttribute",
"Microsoft.AspNetCore.Mvc.ControllerAttribute",
"Microsoft.AspNetCore.Mvc.RouteAttribute"
];

private static readonly HashSet<string> ControllerBaseNames =
private static readonly HashSet<string> AspNetCoreControllerBaseNames =
[
"Microsoft.AspNetCore.Mvc.Controller",
"Microsoft.AspNetCore.Mvc.ControllerBase"
];

private static readonly HashSet<string> ActionAttributes =
private static readonly HashSet<string> AspNetCoreActionAttributes =
[
"Microsoft.AspNetCore.Mvc.HttpGetAttribute",
"Microsoft.AspNetCore.Mvc.HttpPostAttribute",
Expand All @@ -43,6 +43,25 @@ internal static class EndpointDetector
"Microsoft.AspNetCore.Mvc.Routing.HttpMethodAttribute"
];

private static readonly HashSet<string> NetFrameworkControllerBaseNames =
[
// MVC 4/5
"System.Web.Mvc.Controller",
"System.Web.Mvc.ControllerBase",

// Web API 2
"System.Web.Http.ApiController"
];

private static readonly HashSet<string> NetFrameworkNonActionAttributes =
[
// MVC 4/5
"System.Web.Mvc.NonActionAttribute",

// Web API 2
"System.Web.Http.NonActionAttribute"
];

private static readonly HashSet<string> SignalRHubBaseNames =
[
"Microsoft.AspNetCore.SignalR.Hub",
Expand Down Expand Up @@ -74,7 +93,9 @@ internal static ImmutableHashSet<int> GetEndpointMethodTokens(DatadogMetadataRea
bool isPageModel = false;
bool isSignalRHub = false;
bool isCompilerGeneratedType = false;
var isController = IsInheritFromTypesOrHasAttribute(typeDef, metadataReader, ControllerAttributes, ControllerBaseNames);
var isAspNetCoreController = IsInheritFromTypesOrHasAttribute(typeDef, metadataReader, AspNetCoreControllerAttributes, AspNetCoreControllerBaseNames);
var isNetFrameworkController = !isAspNetCoreController && IsInheritFromTypes(typeDef, metadataReader, NetFrameworkControllerBaseNames);
var isController = isAspNetCoreController || isNetFrameworkController;
if (!isController)
{
isPageModel = IsInheritFromTypes(typeDef, metadataReader, PageModelBaseNames);
Expand All @@ -101,7 +122,13 @@ internal static ImmutableHashSet<int> GetEndpointMethodTokens(DatadogMetadataRea
continue;
}

if (isController && HasAttributeFromSet(methodDef.GetCustomAttributes(), metadataReader, ActionAttributes))
if (isAspNetCoreController && HasAttributeFromSet(methodDef.GetCustomAttributes(), metadataReader, AspNetCoreActionAttributes))
{
builder.Add(metadataReader.GetToken(methodHandle));
continue;
}

if (isNetFrameworkController && !HasAttributeFromSet(methodDef.GetCustomAttributes(), metadataReader, NetFrameworkNonActionAttributes))
{
builder.Add(metadataReader.GetToken(methodHandle));
continue;
Expand Down Expand Up @@ -141,7 +168,8 @@ private static bool IsValidMethod(MethodDefinition methodDef)
{
var attributes = methodDef.Attributes;
return (attributes & MethodAttributes.Public) != 0 &&
(attributes & MethodAttributes.Static) == 0;
(attributes & MethodAttributes.Static) == 0 &&
(attributes & MethodAttributes.SpecialName) == 0;
}

private static bool IsInheritFromTypesOrHasAttribute(TypeDefinition typeDef, MetadataReader reader, HashSet<string> attributesNames, HashSet<string> baseTypeNames)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -483,9 +483,9 @@ std::vector<CallTargetDefinition3> callTargets =
#if _WIN32
{(WCHAR*)WStr("System.Web.Mvc"),(WCHAR*)WStr("System.Web.Mvc.Async.AsyncControllerActionInvoker"),(WCHAR*)WStr("BeginInvokeAction"),sig145,5,4,0,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.AsyncControllerActionInvoker_BeginInvokeAction_Integration"),CallTargetKind::Default,1,1},
{(WCHAR*)WStr("System.Web.Mvc"),(WCHAR*)WStr("System.Web.Mvc.Async.AsyncControllerActionInvoker"),(WCHAR*)WStr("EndInvokeAction"),sig124,2,4,0,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.AsyncControllerActionInvoker_EndInvokeAction_Integration"),CallTargetKind::Default,1,1},
{(WCHAR*)WStr("System.Web.Mvc"),(WCHAR*)WStr("System.Web.Mvc.ControllerActionInvoker"),(WCHAR*)WStr("InvokeActionMethod"),sig394,4,4,0,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ControllerActionInvoker_InvokeAction_Integration"),CallTargetKind::Default,6,1},
{(WCHAR*)WStr("System.Web.Mvc"),(WCHAR*)WStr("System.Web.Mvc.ControllerActionInvoker"),(WCHAR*)WStr("InvokeActionMethod"),sig394,4,4,0,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ControllerActionInvoker_InvokeAction_Integration"),CallTargetKind::Default,7,1},
{(WCHAR*)WStr("System.Web.Http"),(WCHAR*)WStr("System.Web.Http.ApiController"),(WCHAR*)WStr("ExecuteAsync"),sig299,3,5,1,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ApiController_ExecuteAsync_Integration"),CallTargetKind::Default,1,1},
{(WCHAR*)WStr("System.Web.Http"),(WCHAR*)WStr("System.Web.Http.Controllers.ReflectedHttpActionDescriptor"),(WCHAR*)WStr("ExecuteAsync"),sig298,4,5,1,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ReflectedHttpActionDescriptor_ExecuteAsync_Integration"),CallTargetKind::Default,6,1},
{(WCHAR*)WStr("System.Web.Http"),(WCHAR*)WStr("System.Web.Http.Controllers.ReflectedHttpActionDescriptor"),(WCHAR*)WStr("ExecuteAsync"),sig298,4,5,1,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ReflectedHttpActionDescriptor_ExecuteAsync_Integration"),CallTargetKind::Default,7,1},
{(WCHAR*)WStr("System.Web.Http"),(WCHAR*)WStr("System.Web.Http.ExceptionHandling.ExceptionHandlerExtensions"),(WCHAR*)WStr("HandleAsync"),sig300,4,5,1,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ExceptionHandlerExtensions_HandleAsync_Integration"),CallTargetKind::Default,1,1},
#endif
{(WCHAR*)WStr("AWSSDK.DynamoDBv2"),(WCHAR*)WStr("Amazon.DynamoDBv2.AmazonDynamoDBClient"),(WCHAR*)WStr("BatchGetItem"),sig008,2,3,0,0,4,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AWS.DynamoDb.BatchGetItemIntegration"),CallTargetKind::Default,1,15},
Expand Down
Loading
Loading