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

Add metrics for datacollector.exe - provides information about profilers #2705

Merged
merged 12 commits into from
Jan 25, 2021
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
2 changes: 1 addition & 1 deletion scripts/build/TestPlatform.Dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<JsonNetVersion>9.0.1</JsonNetVersion>
<MoqVersion>4.7.63</MoqVersion>
<TestPlatformExternalsVersion>16.9.0-preview-4267359</TestPlatformExternalsVersion>
<CodeCoverageExternalsVersion>16.9.0-beta.21064.1</CodeCoverageExternalsVersion>
<CodeCoverageExternalsVersion>16.9.0-beta.21069.2</CodeCoverageExternalsVersion>
<MicrosoftFakesVersion>16.9.0-beta.20628.1</MicrosoftFakesVersion>

<MicrosoftBuildPackageVersion>16.0.461</MicrosoftBuildPackageVersion>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollection
{
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;

/// <summary>
/// Payload object that is used to exchange data between datacollector process and runner process.
/// </summary>
[DataContract]
public class AfterTestRunEndResult
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we require this to be public?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. When tried internal than I got:
Severity Code Description Project File Line Suppression State
Error CS0050 Inconsistent accessibility: return type 'AfterTestRunEndResult' is less accessible than method 'DataCollectionRequestSender.SendAfterTestRunEndAndGetResult(ITestMessageEventHandler, bool)' Microsoft.TestPlatform.CommunicationUtilities (net451), Microsoft.TestPlatform.CommunicationUtilities (netstandard1.3), Microsoft.TestPlatform.CommunicationUtilities (netstandard2.0) D:\vstest\src\Microsoft.TestPlatform.CommunicationUtilities\DataCollectionRequestSender.cs 153 Active

SendAfterTestRunEndAndGetResult is part of interface so it cannot be done internal

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, that is sad. Adding @cvpoienaru to ensure that we aren't making/breaking any compat promises with this. From what I know, I think we are good.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've already discussed it with @cvpoienaru

{
/// <summary>
/// Initializes a new instance of the <see cref="AfterTestRunEndResult"/> class.
/// </summary>
/// <param name="environmentVariables">
/// The collection of attachment sets.
/// </param>
/// <param name="dataCollectionEventsPort">
jakubch1 marked this conversation as resolved.
Show resolved Hide resolved
/// The metrics.
/// </param>
public AfterTestRunEndResult(Collection<AttachmentSet> attachmentSets, IDictionary<string, object> metrics)
{
this.AttachmentSets = attachmentSets;
this.Metrics = metrics;
}

/// <summary>
/// Gets the environment variable dictionary.
jakubch1 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
[DataMember]
public Collection<AttachmentSet> AttachmentSets { get; private set; }

/// <summary>
/// Get the Metrics
/// </summary>
[DataMember]
public IDictionary<string, object> Metrics { get; private set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollector
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;

using Microsoft.VisualStudio.TestPlatform.Common.DataCollector.Interfaces;
using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework;
using Microsoft.VisualStudio.TestPlatform.Common.Logging;
using Microsoft.VisualStudio.TestPlatform.Common.Telemetry;
using Microsoft.VisualStudio.TestPlatform.Common.Utilities;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
Expand Down Expand Up @@ -61,13 +62,18 @@ internal class DataCollectionManager : IDataCollectionManager
/// </summary>
private DataCollectorExtensionManager dataCollectorExtensionManager;

/// <summary>
/// Request data
/// </summary>
private IDataCollectionTelemetryManager dataCollectionTelemetryManager;

/// <summary>
/// Initializes a new instance of the <see cref="DataCollectionManager"/> class.
/// </summary>
/// <param name="messageSink">
/// The message Sink.
/// </param>
internal DataCollectionManager(IMessageSink messageSink) : this(new DataCollectionAttachmentManager(), messageSink)
internal DataCollectionManager(IMessageSink messageSink, IRequestData requestData) : this(new DataCollectionAttachmentManager(), messageSink, new DataCollectionTelemetryManager(requestData))
{
}

Expand All @@ -83,13 +89,14 @@ internal DataCollectionManager(IMessageSink messageSink) : this(new DataCollecti
/// <remarks>
/// The constructor is not public because the factory method should be used to get instances of this class.
/// </remarks>
protected DataCollectionManager(IDataCollectionAttachmentManager datacollectionAttachmentManager, IMessageSink messageSink)
protected DataCollectionManager(IDataCollectionAttachmentManager datacollectionAttachmentManager, IMessageSink messageSink, IDataCollectionTelemetryManager dataCollectionTelemetryManager)
{
this.attachmentManager = datacollectionAttachmentManager;
this.messageSink = messageSink;
this.events = new TestPlatformDataCollectionEvents();
this.dataCollectorExtensionManager = null;
this.RunDataCollectors = new Dictionary<Type, DataCollectorInformation>();
this.dataCollectionTelemetryManager = dataCollectionTelemetryManager;
}

/// <summary>
Expand Down Expand Up @@ -136,7 +143,12 @@ public static DataCollectionManager Create(IMessageSink messageSink)
{
if (Instance == null)
{
Instance = new DataCollectionManager(messageSink);
var requestData = new RequestData
{
MetricsCollection = new MetricsCollection(),
IsTelemetryOptedIn = true
jakubch1 marked this conversation as resolved.
Show resolved Hide resolved
};
Instance = new DataCollectionManager(messageSink, requestData);
}
}
}
Expand Down Expand Up @@ -330,6 +342,8 @@ public Collection<AttachmentSet> TestCaseEnded(TestCaseEndEventArgs testCaseEndE
return new Collection<AttachmentSet>(result);
}

public IRequestData RequestData => this.dataCollectionTelemetryManager.RequestData;

/// <summary>
/// The dispose.
/// </summary>
Expand Down Expand Up @@ -650,6 +664,7 @@ private void AddCollectorEnvironmentVariables(
if (string.Equals(namevaluepair.Value, alreadyRequestedVariable.Value, StringComparison.Ordinal))
{
alreadyRequestedVariable.AddRequestingDataCollector(collectorFriendlyName);
dataCollectionTelemetryManager.OnEnvironmentVariableAdded(dataCollectionWrapper, namevaluepair.Key, namevaluepair.Value);
}
else
{
Expand All @@ -671,7 +686,9 @@ private void AddCollectorEnvironmentVariables(
else
{
dataCollectionWrapper.Logger.LogError(this.dataCollectionEnvironmentContext.SessionDataCollectionContext, message);
}
}

dataCollectionTelemetryManager.OnEnvironmentVariableConflict(dataCollectionWrapper, namevaluepair.Key, alreadyRequestedVariable.Value);
}
}
else
Expand All @@ -685,6 +702,7 @@ private void AddCollectorEnvironmentVariables(
dataCollectorEnvironmentVariables.Add(
namevaluepair.Key,
new DataCollectionEnvironmentVariable(namevaluepair, collectorFriendlyName));
dataCollectionTelemetryManager.OnEnvironmentVariableAdded(dataCollectionWrapper, namevaluepair.Key, namevaluepair.Value);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.VisualStudio.TestPlatform.Common.DataCollector.Interfaces;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using System;
using System.Linq;

namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollector
{
/// <summary>
/// Stores and provides telemetry information for data collection.
/// </summary>
internal class DataCollectionTelemetryManager : IDataCollectionTelemetryManager
AbhitejJohn marked this conversation as resolved.
Show resolved Hide resolved
{
private const string CorProfilerVariable = "COR_PROFILER";
private const string CoreClrProfilerVariable = "CORECLR_PROFILER";
private const string ClrIeInstrumentationMethodConfigurationPrefix32Variable = "MicrosoftInstrumentationEngine_ConfigPath32_";
private const string ClrIeInstrumentationMethodConfigurationPrefix64Variable = "MicrosoftInstrumentationEngine_ConfigPath64_";

private const string VanguardProfilerGuid = "{E5F256DC-7959-4DD6-8E4F-C11150AB28E0}";
private const string ClrIeProfilerGuid = "{324F817A-7420-4E6D-B3C1-143FBED6D855}";
private const string IntellitraceProfilerGuid = "{9317ae81-bcd8-47b7-aaa1-a28062e41c71}";

private const string VanguardProfilerName = "vanguard";
private const string ClrIeProfilerName = "clrie";
private const string IntellitraceProfilerName = "intellitrace";
private const string UnknownProfilerName = "unknown";
private const string OverwrittenProfilerName = "overwritten";

private const string CorProfilerTelemetryTemplate = "VS.TestPlatform.DataCollector.{0}.CorProfiler";
jakubch1 marked this conversation as resolved.
Show resolved Hide resolved
private const string CoreClrProfilerTelemetryTemplate = "VS.TestPlatform.DataCollector.{0}.CoreClrProfiler";

private readonly IRequestData requestData;

internal DataCollectionTelemetryManager(IRequestData requestData)
{
this.requestData = requestData;
}

/// <inheritdoc/>
public void OnEnvironmentVariableAdded(DataCollectorInformation dataCollectorInformation, string name, string value)
{
AddProfilerMetricForNewVariable(CorProfilerVariable, CorProfilerTelemetryTemplate, dataCollectorInformation, name, value);
AddProfilerMetricForNewVariable(CoreClrProfilerVariable, CoreClrProfilerTelemetryTemplate, dataCollectorInformation, name, value);
}

/// <inheritdoc/>
public void OnEnvironmentVariableConflict(DataCollectorInformation dataCollectorInformation, string name, string existingValue)
{
AddProfilerMetricForConflictedVariable(CorProfilerVariable, CorProfilerTelemetryTemplate, dataCollectorInformation, name, existingValue);
AddProfilerMetricForConflictedVariable(CoreClrProfilerVariable, CoreClrProfilerTelemetryTemplate, dataCollectorInformation, name, existingValue);
}

/// <inheritdoc/>
public IRequestData RequestData => requestData;

private void AddProfilerMetricForNewVariable(string profilerVariable, string telemetryTemplateName, DataCollectorInformation dataCollectorInformation, string name, string value)
{
if (!string.Equals(profilerVariable, name, StringComparison.Ordinal))
{
return;
}

requestData.MetricsCollection.Add(string.Format(telemetryTemplateName, dataCollectorInformation.DataCollectorConfig?.TypeUri?.ToString()), GetProfilerName(value));
}

private void AddProfilerMetricForConflictedVariable(string profilerVariable, string telemetryTemplateName, DataCollectorInformation dataCollectorInformation, string name, string existingValue)
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider: Could we merge with the above and make this a for-loop over the environment variables of interest?

Copy link
Member Author

Choose a reason for hiding this comment

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

In such case we need to copy logic which we already have to detect conflicts and so on. I would keep it like it is now. I want to keep this class making only simple metrics, nothing more

Copy link
Member Author

Choose a reason for hiding this comment

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

I've made it a little bit simpler. We have 2 methods: RecordEnvVariableAddition and RecordEnvVariableConflict.

{
if (!string.Equals(profilerVariable, name, StringComparison.Ordinal))
{
return;
}

if (string.Equals(existingValue, ClrIeProfilerGuid, StringComparison.OrdinalIgnoreCase))
{
if (dataCollectorInformation.TestExecutionEnvironmentVariables != null &&
dataCollectorInformation.TestExecutionEnvironmentVariables.Any(pair => pair.Key.StartsWith(ClrIeInstrumentationMethodConfigurationPrefix32Variable)) &&
jakubch1 marked this conversation as resolved.
Show resolved Hide resolved
dataCollectorInformation.TestExecutionEnvironmentVariables.Any(pair => pair.Key.StartsWith(ClrIeInstrumentationMethodConfigurationPrefix64Variable)))
{
requestData.MetricsCollection.Add(string.Format(telemetryTemplateName, dataCollectorInformation.DataCollectorConfig?.TypeUri?.ToString()), ClrIeProfilerName);
return;
}
}

requestData.MetricsCollection.Add(string.Format(telemetryTemplateName, dataCollectorInformation.DataCollectorConfig?.TypeUri?.ToString()), OverwrittenProfilerName);
AbhitejJohn marked this conversation as resolved.
Show resolved Hide resolved
}

private static string GetProfilerName(string profilerGuid)
{
if (string.Equals(profilerGuid, VanguardProfilerGuid, StringComparison.OrdinalIgnoreCase))
jakubch1 marked this conversation as resolved.
Show resolved Hide resolved
{
return VanguardProfilerName;
}
else if (string.Equals(profilerGuid, ClrIeProfilerGuid, StringComparison.OrdinalIgnoreCase))
{
return ClrIeProfilerName;
}
else if (string.Equals(profilerGuid, IntellitraceProfilerGuid, StringComparison.OrdinalIgnoreCase))
{
return IntellitraceProfilerName;
}

return UnknownProfilerName;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollector.Interfaces
using System.Collections.ObjectModel;

using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;

/// <summary>
Expand Down Expand Up @@ -66,5 +67,13 @@ internal interface IDataCollectionManager : IDisposable
/// Collection of session attachmentSet.
/// </returns>
Collection<AttachmentSet> SessionEnded(bool isCancelled);

/// <summary>
/// Provides telemetry object.
/// </summary>
/// <returns>
/// IRequestData object.
/// </returns>
IRequestData RequestData { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;

namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollector.Interfaces
{
/// <summary>
/// The IDataCollectionTelemetryManager Interface.
/// </summary>
internal interface IDataCollectionTelemetryManager
{
/// <summary>
/// Stores telemetry regarding environment variable added.
/// </summary>
/// <param name="dataCollectorInformation">
/// Data collector information which requested environment variable.
/// </param>
/// <param name="name">
/// Environment variable name.
/// </param>
/// <param name="value">
/// Environment variable value.
/// </param>
void OnEnvironmentVariableAdded(DataCollectorInformation dataCollectorInformation, string name, string value);

/// <summary>
/// Stores telemetry regarding environment variable is conflicting.
/// </summary>
/// <param name="dataCollectorInformation">
/// Data collector information which requested environment variable.
/// </param>
/// <param name="name">
/// Environment variable name.
/// </param>
/// <param name="existingValue">
/// Environment variable value that was requested previously.
/// </param>
void OnEnvironmentVariableConflict(DataCollectorInformation dataCollectorInformation, string name, string existingValue);

/// <summary>
/// Provides telemetry object.
/// </summary>
/// <returns>
/// IRequestData object.
/// </returns>
IRequestData RequestData { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.DataCollect
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers;
Expand Down Expand Up @@ -372,12 +373,13 @@ private void HandleAfterTestRunEnd(Message message)
}

var attachmentsets = this.dataCollectionManager.SessionEnded(isCancelled);
var afterTestRunEndResult = new AfterTestRunEndResult(attachmentsets, this.dataCollectionManager.RequestData?.MetricsCollection?.Metrics);

// Dispose all datacollectors before sending attachments to vstest.console process.
// As datacollector process exits itself on parent process(vstest.console) exits.
this.dataCollectionManager?.Dispose();

this.communicationManager.SendMessage(MessageType.AfterTestRunEndResult, attachmentsets);
this.communicationManager.SendMessage(MessageType.AfterTestRunEndResult, afterTestRunEndResult);
EqtTrace.Info("DataCollectionRequestHandler.ProcessRequests : Session End message received from server. Closing the connection.");

this.Close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.DataCollection
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Net;

Expand Down Expand Up @@ -151,10 +150,10 @@ public BeforeTestRunStartResult SendBeforeTestRunStartAndGetResult(string settin
}

/// <inheritdoc/>
public Collection<AttachmentSet> SendAfterTestRunEndAndGetResult(ITestMessageEventHandler runEventsHandler, bool isCancelled)
public AfterTestRunEndResult SendAfterTestRunEndAndGetResult(ITestMessageEventHandler runEventsHandler, bool isCancelled)
{
var isDataCollectionComplete = false;
Collection<AttachmentSet> attachmentSets = null;
AfterTestRunEndResult result = null;

if (EqtTrace.IsVerboseEnabled)
{
Expand All @@ -181,12 +180,12 @@ public Collection<AttachmentSet> SendAfterTestRunEndAndGetResult(ITestMessageEve
}
else if (message.MessageType == MessageType.AfterTestRunEndResult)
{
attachmentSets = this.dataSerializer.DeserializePayload<Collection<AttachmentSet>>(message);
result = this.dataSerializer.DeserializePayload<AfterTestRunEndResult>(message);
isDataCollectionComplete = true;
}
}

return attachmentSets;
return result;
}

private void LogDataCollectorMessage(DataCollectionMessageEventArgs dataCollectionMessageEventArgs, ITestMessageEventHandler requestHandler)
Expand Down
Loading