Skip to content
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

[CIVisibility] TestPlatform AssemblyResolver .ctor integration #5088

Merged
Merged
Show file tree
Hide file tree
Changes from 3 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
3 changes: 0 additions & 3 deletions tracer/build/_build/Build.Steps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2375,9 +2375,6 @@ string NormalizedPath(AbsolutePath ap)
new (@".*DD_GIT_COMMIT_SHA must be a full-length git SHA.*", RegexOptions.Compiled),
new (@".*Timeout occurred when flushing spans.*", RegexOptions.Compiled),
new (@".*ITR: .*", RegexOptions.Compiled),
// Error caused for race condition on TestPlatform SDK triggered by CI Visibility
// https://github.com/microsoft/vstest/blob/v16.7.1/src/Microsoft.TestPlatform.Common/Utilities/AssemblyResolver.cs#L67-L70
new (@".*Error detecting and reconfiguring git repository for shallow clone. System.IO.FileLoadException: Could not load file or assembly.*", RegexOptions.Compiled),
// This one is annoying but we _think_ due to a dodgy named pipes implementation, so ignoring for now
new(@".*An error occurred while sending data to the agent at \\\\\.\\pipe\\trace-.*The operation has timed out.*", RegexOptions.Compiled),
new(@".*An error occurred while sending data to the agent at \\\\\.\\pipe\\metrics-.*The operation has timed out.*", RegexOptions.Compiled),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
<type fullname="Microsoft.Net.Http.Headers.SameSiteMode" />
<type fullname="Microsoft.Net.Http.Headers.SetCookieHeaderValue" />
</assembly>
<assembly fullname="Microsoft.VisualStudio.TestPlatform.Common" />
<assembly fullname="Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter" />
<assembly fullname="Microsoft.VisualStudio.TestPlatform.TestFramework" />
<assembly fullname="MongoDB.Driver.Core" />
Expand Down
8 changes: 8 additions & 0 deletions tracer/src/Datadog.Trace/Ci/IntelligentTestRunnerClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,10 @@ private void CheckResponseStatusCode(IApiResponse response, string? responseCont

private async Task<T> WithRetries<T, TState>(Func<TState, bool, Task<T>> sendDelegate, TState state, int numOfRetries)
{
// Because there's an integration to Http requests we need to make sure that if the AssemblyResolver.ctor of the TestPlatform started then it has finished
// before continuing to avoid a race condition.
await ClrProfiler.AutoInstrumentation.Testing.AssemblyResolverCtorIntegration.WaitForCallToBeCompletedAsync().ConfigureAwait(false);

var retryCount = 1;
var sleepDuration = 100; // in milliseconds

Expand Down Expand Up @@ -944,6 +948,10 @@ private async Task<string> GetBranchNameAsync()

private async Task<ProcessHelpers.CommandOutput?> RunGitCommandAsync(string arguments, MetricTags.CIVisibilityCommands ciVisibilityCommand, string? input = null)
{
// Because there's an integration to Process.Start we need to make sure that if the AssemblyResolver.ctor of the TestPlatform started then it has finished
// before continuing to avoid a race condition.
await ClrProfiler.AutoInstrumentation.Testing.AssemblyResolverCtorIntegration.WaitForCallToBeCompletedAsync().ConfigureAwait(false);

TelemetryFactory.Metrics.RecordCountCIVisibilityGitCommand(ciVisibilityCommand);
var sw = System.Diagnostics.Stopwatch.StartNew();
var gitOutput = await ProcessHelpers.RunCommandAsync(new ProcessHelpers.Command("git", arguments, _workingDirectory), input).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// <copyright file="AssemblyResolverCtorIntegration.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>
#nullable enable

using System;
using System.ComponentModel;
using System.Threading.Tasks;
using Datadog.Trace.ClrProfiler.CallTarget;

namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing;

/// <summary>
/// System.Void Microsoft.VisualStudio.TestPlatform.Common.Utilities.AssemblyResolver::.ctor(System.Collections.Generic.IEnumerable`1[System.String]) calltarget instrumentation
/// </summary>
[InstrumentMethod(
AssemblyName = "Microsoft.VisualStudio.TestPlatform.Common",
TypeName = "Microsoft.VisualStudio.TestPlatform.Common.Utilities.AssemblyResolver",
MethodName = ".ctor",
ReturnTypeName = ClrNames.Void,
ParameterTypeNames = ["System.Collections.Generic.IEnumerable`1[System.String]"],
MinimumVersion = "15.0.0",
MaximumVersion = "15.*.*",
IntegrationName = IntegrationName)]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public class AssemblyResolverCtorIntegration
{
private const string IntegrationName = "TestPlatformAssemblyResolver";

private static TaskCompletionSource<bool>? _callHasBeenCompletedTaskCompletionSource;

/// <summary>
/// OnMethodBegin callback
/// </summary>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <typeparam name="TArg1">Type of the argument 1 (System.Collections.Generic.IEnumerable`1[System.String])</typeparam>
/// <param name="instance">Instance value, aka `this` of the instrumented method</param>
/// <param name="directories">Instance of System.Collections.Generic.IEnumerable`1[System.String]</param>
/// <returns>Calltarget state value</returns>
internal static CallTargetState OnMethodBegin<TTarget, TArg1>(TTarget instance, ref TArg1 directories)
{
Common.Log.Debug("Microsoft.VisualStudio.TestPlatform.Common.Utilities.AssemblyResolver.ctor started.");
_callHasBeenCompletedTaskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any risk of new AssemblyResolver() being called more than once? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's called twice but for different scenarios, one as of them for the IDE an scenario that is not covered by CI Visibility. So we are safe.

return CallTargetState.GetDefault();
}

/// <summary>
/// OnMethodEnd callback
/// </summary>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <param name="instance">Instance value, aka `this` of the instrumented method.</param>
/// <param name="exception">Exception instance in case the original code threw an exception.</param>
/// <param name="state">Calltarget state value</param>
/// <returns>A return value, in an async scenario will be T of Task of T</returns>
internal static CallTargetReturn OnMethodEnd<TTarget>(TTarget instance, Exception? exception, in CallTargetState state)
{
_callHasBeenCompletedTaskCompletionSource?.TrySetResult(exception is null);
Common.Log.Debug("Microsoft.VisualStudio.TestPlatform.Common.Utilities.AssemblyResolver.ctor finished.");
return CallTargetReturn.GetDefault();
}

internal static Task WaitForCallToBeCompletedAsync()
{
if (_callHasBeenCompletedTaskCompletionSource?.Task is { } callTask)
{
if (!callTask.IsCompleted)
{
return InternalWaitForCallToBeCompletedAsync(callTask);
}

Common.Log.Debug("Microsoft.VisualStudio.TestPlatform.Common.Utilities.AssemblyResolver.ctor already ran.");
}

return Task.CompletedTask;

static async Task InternalWaitForCallToBeCompletedAsync(Task<bool> callTask)
{
Common.Log.Debug("Waiting for Microsoft.VisualStudio.TestPlatform.Common.Utilities.AssemblyResolver.ctor to be completed...");
await callTask.ConfigureAwait(false);
Common.Log.Debug("Call to Microsoft.VisualStudio.TestPlatform.Common.Utilities.AssemblyResolver.ctor is completed.");
}
}
}
1 change: 1 addition & 0 deletions tracer/src/Datadog.Trace/Configuration/IntegrationId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,6 @@ internal enum IntegrationId
Remoting,
TrustBoundaryViolation,
UnvalidatedRedirect,
TestPlatformAssemblyResolver,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal static partial class IntegrationIdExtensions
/// The number of members in the enum.
/// This is a non-distinct count of defined names.
/// </summary>
public const int Length = 59;
public const int Length = 60;

/// <summary>
/// Returns the string representation of the <see cref="Datadog.Trace.Configuration.IntegrationId"/> value.
Expand Down Expand Up @@ -89,6 +89,7 @@ public static string ToStringFast(this Datadog.Trace.Configuration.IntegrationId
Datadog.Trace.Configuration.IntegrationId.Remoting => nameof(Datadog.Trace.Configuration.IntegrationId.Remoting),
Datadog.Trace.Configuration.IntegrationId.TrustBoundaryViolation => nameof(Datadog.Trace.Configuration.IntegrationId.TrustBoundaryViolation),
Datadog.Trace.Configuration.IntegrationId.UnvalidatedRedirect => nameof(Datadog.Trace.Configuration.IntegrationId.UnvalidatedRedirect),
Datadog.Trace.Configuration.IntegrationId.TestPlatformAssemblyResolver => nameof(Datadog.Trace.Configuration.IntegrationId.TestPlatformAssemblyResolver),
_ => value.ToString(),
};

Expand Down Expand Up @@ -161,6 +162,7 @@ public static Datadog.Trace.Configuration.IntegrationId[] GetValues()
Datadog.Trace.Configuration.IntegrationId.Remoting,
Datadog.Trace.Configuration.IntegrationId.TrustBoundaryViolation,
Datadog.Trace.Configuration.IntegrationId.UnvalidatedRedirect,
Datadog.Trace.Configuration.IntegrationId.TestPlatformAssemblyResolver,
};

/// <summary>
Expand Down Expand Up @@ -233,5 +235,6 @@ public static string[] GetNames()
nameof(Datadog.Trace.Configuration.IntegrationId.Remoting),
nameof(Datadog.Trace.Configuration.IntegrationId.TrustBoundaryViolation),
nameof(Datadog.Trace.Configuration.IntegrationId.UnvalidatedRedirect),
nameof(Datadog.Trace.Configuration.IntegrationId.TestPlatformAssemblyResolver),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,9 @@ static InstrumentationDefinitions()
new (NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("StackExchange.Redis.StrongName"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("StackExchange.Redis.RedisTransaction"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("ExecuteAsync"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16StringArray("System.Threading.Tasks.Task`1<!!0>", "StackExchange.Redis.Message", "StackExchange.Redis.ResultProcessor`1[!!0]", "StackExchange.Redis.ServerEndPoint"), 4, 1, 0, 0, 2, 65535, 65535, NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String(assemblyFullName), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("Datadog.Trace.ClrProfiler.AutoInstrumentation.Redis.StackExchange.RedisExecuteAsyncIntegration"), 0, 1),
new (NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("StackExchange.Redis.StrongName"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("StackExchange.Redis.RedisTransaction"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("ExecuteAsync"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16StringArray("System.Threading.Tasks.Task`1<!!0>", "StackExchange.Redis.Message", "StackExchange.Redis.ResultProcessor`1[!!0]", "!!0", "StackExchange.Redis.ServerEndPoint"), 5, 2, 0, 0, 2, 65535, 65535, NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String(assemblyFullName), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("Datadog.Trace.ClrProfiler.AutoInstrumentation.Redis.StackExchange.RedisExecuteAsyncIntegration_2_6_48"), 0, 1),

// TestPlatformAssemblyResolver
new (NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("Microsoft.VisualStudio.TestPlatform.Common"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("Microsoft.VisualStudio.TestPlatform.Common.Utilities.AssemblyResolver"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String(".ctor"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16StringArray("System.Void", "System.Collections.Generic.IEnumerable`1[System.String]"), 2, 15, 0, 0, 15, 65535, 65535, NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String(assemblyFullName), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.AssemblyResolverCtorIntegration"), 0, 1),

// Wcf
new (NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("System.ServiceModel"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("System.ServiceModel.Dispatcher.AsyncMethodInvoker"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("InvokeBegin"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16StringArray("System.IAsyncResult", "System.Object", "System.Object[]", "System.AsyncCallback", "System.Object"), 5, 4, 0, 0, 4, 65535, 65535, NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String(assemblyFullName), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf.AsyncMethodInvoker_InvokeBegin_Integration"), 0, 1),
new (NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("System.ServiceModel"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("System.ServiceModel.Dispatcher.AsyncMethodInvoker"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("InvokeEnd"), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16StringArray("System.Object", "System.Object", "System.Object[]&", "System.IAsyncResult"), 4, 4, 0, 0, 4, 65535, 65535, NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String(assemblyFullName), NativeCallTargetUnmanagedMemoryHelper.AllocateAndWriteUtf16String("Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf.AsyncMethodInvoker_InvokeEnd_Integration"), 0, 1),
Expand Down Expand Up @@ -822,6 +825,8 @@ internal static bool IsInstrumentedAssembly(string assemblyName)
or "Datadog.Trace.ClrProfiler.AutoInstrumentation.Redis.StackExchange.RedisExecuteAsyncIntegration"
or "Datadog.Trace.ClrProfiler.AutoInstrumentation.Redis.StackExchange.RedisExecuteAsyncIntegration_2_6_48"
=> Datadog.Trace.Configuration.IntegrationId.StackExchangeRedis,
"Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.AssemblyResolverCtorIntegration"
=> Datadog.Trace.Configuration.IntegrationId.TestPlatformAssemblyResolver,
"Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf.AsyncMethodInvoker_InvokeBegin_Integration"
or "Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf.AsyncMethodInvoker_InvokeEnd_Integration"
or "Datadog.Trace.ClrProfiler.AutoInstrumentation.Wcf.ChannelHandlerIntegration"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal static partial class IntegrationIdExtensions
/// The number of members in the enum.
/// This is a non-distinct count of defined names.
/// </summary>
public const int Length = 59;
public const int Length = 60;

/// <summary>
/// Returns the string representation of the <see cref="Datadog.Trace.Configuration.IntegrationId"/> value.
Expand Down Expand Up @@ -89,6 +89,7 @@ public static string ToStringFast(this Datadog.Trace.Configuration.IntegrationId
Datadog.Trace.Configuration.IntegrationId.Remoting => nameof(Datadog.Trace.Configuration.IntegrationId.Remoting),
Datadog.Trace.Configuration.IntegrationId.TrustBoundaryViolation => nameof(Datadog.Trace.Configuration.IntegrationId.TrustBoundaryViolation),
Datadog.Trace.Configuration.IntegrationId.UnvalidatedRedirect => nameof(Datadog.Trace.Configuration.IntegrationId.UnvalidatedRedirect),
Datadog.Trace.Configuration.IntegrationId.TestPlatformAssemblyResolver => nameof(Datadog.Trace.Configuration.IntegrationId.TestPlatformAssemblyResolver),
_ => value.ToString(),
};

Expand Down Expand Up @@ -161,6 +162,7 @@ public static Datadog.Trace.Configuration.IntegrationId[] GetValues()
Datadog.Trace.Configuration.IntegrationId.Remoting,
Datadog.Trace.Configuration.IntegrationId.TrustBoundaryViolation,
Datadog.Trace.Configuration.IntegrationId.UnvalidatedRedirect,
Datadog.Trace.Configuration.IntegrationId.TestPlatformAssemblyResolver,
};

/// <summary>
Expand Down Expand Up @@ -233,5 +235,6 @@ public static string[] GetNames()
nameof(Datadog.Trace.Configuration.IntegrationId.Remoting),
nameof(Datadog.Trace.Configuration.IntegrationId.TrustBoundaryViolation),
nameof(Datadog.Trace.Configuration.IntegrationId.UnvalidatedRedirect),
nameof(Datadog.Trace.Configuration.IntegrationId.TestPlatformAssemblyResolver),
};
}
Loading
Loading