Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/release/dev17.12' into merges/re…
Browse files Browse the repository at this point in the history
…lease/dev17.12-to-main
  • Loading branch information
phil-allen-msft committed Sep 26, 2024
2 parents 3e0da42 + eb2b7d4 commit af590ea
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 62 deletions.
8 changes: 4 additions & 4 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,14 @@
<Uri>https://dev.azure.com/dnceng/internal/_git/dotnet-runtime</Uri>
<Sha>3a25a7f1cc446b60678ed25c9d829420d6321eba</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="9.0.0-beta.24453.1">
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="9.0.0-beta.24466.2">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>dd332f2d4e21daa8b79f84251ab156af9a0b11b2</Sha>
<Sha>04b9022eba9c184a8036328af513c22e6949e8b6</Sha>
</Dependency>
<!-- Intermediate is necessary for source build. -->
<Dependency Name="Microsoft.SourceBuild.Intermediate.arcade" Version="9.0.0-beta.24453.1">
<Dependency Name="Microsoft.SourceBuild.Intermediate.arcade" Version="9.0.0-beta.24466.2">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>dd332f2d4e21daa8b79f84251ab156af9a0b11b2</Sha>
<Sha>04b9022eba9c184a8036328af513c22e6949e8b6</Sha>
<SourceBuild RepoName="arcade" ManagedOnly="true" />
</Dependency>
<Dependency Name="Microsoft.DotNet.XliffTasks" Version="1.0.0-beta.23475.1" CoherentParentDependency="Microsoft.DotNet.Arcade.Sdk">
Expand Down
4 changes: 2 additions & 2 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
<PropertyGroup Label="Automated">
<MicrosoftNETCoreBrowserDebugHostTransportPackageVersion>6.0.2-servicing.22064.6</MicrosoftNETCoreBrowserDebugHostTransportPackageVersion>
<MicrosoftNETCorePlatformsPackageVersion>6.0.1</MicrosoftNETCorePlatformsPackageVersion>
<MicrosoftSourceBuildIntermediatesourcebuildreferencepackagesPackageVersion>10.0.0-alpha.1.24467.1</MicrosoftSourceBuildIntermediatesourcebuildreferencepackagesPackageVersion>
<MicrosoftSourceBuildIntermediatearcadePackageVersion>9.0.0-beta.24453.1</MicrosoftSourceBuildIntermediatearcadePackageVersion>
<MicrosoftSourceBuildIntermediatesourcebuildreferencepackagesPackageVersion>9.0.0-alpha.1.24304.1</MicrosoftSourceBuildIntermediatesourcebuildreferencepackagesPackageVersion>
<MicrosoftSourceBuildIntermediatearcadePackageVersion>9.0.0-beta.24466.2</MicrosoftSourceBuildIntermediatearcadePackageVersion>
<MicrosoftDotNetXliffTasksPackageVersion>1.0.0-beta.23475.1</MicrosoftDotNetXliffTasksPackageVersion>
<MicrosoftSourceBuildIntermediatexlifftasksPackageVersion>1.0.0-beta.23475.1</MicrosoftSourceBuildIntermediatexlifftasksPackageVersion>
<MicrosoftNetCompilersToolsetPackageVersion>4.12.0-3.24466.4</MicrosoftNetCompilersToolsetPackageVersion>
Expand Down
6 changes: 3 additions & 3 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"tools": {
"dotnet": "9.0.100-preview.7.24407.12",
"dotnet": "9.0.100-rc.1.24452.12",
"runtimes": {
"dotnet": [
"2.1.30",
Expand All @@ -17,12 +17,12 @@
}
},
"sdk": {
"version": "9.0.100-preview.7.24407.12",
"version": "9.0.100-rc.1.24452.12",
"allowPrerelease": false,
"rollForward": "latestPatch"
},
"msbuild-sdks": {
"Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24453.1",
"Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24466.2",
"Microsoft.Build.NoTargets": "3.7.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.VisualStudio.Telemetry;
using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.AspNetCore.Razor;
using System.IO;


#if DEBUG
using System.Linq;
Expand All @@ -16,6 +20,16 @@ namespace Microsoft.VisualStudio.Razor.Telemetry;

internal abstract class TelemetryReporter : ITelemetryReporter
{
private const string CodeAnalysisNamespace = nameof(Microsoft) + "." + nameof(CodeAnalysis);
private const string AspNetCoreNamespace = nameof(Microsoft) + "." + nameof(AspNetCore);
private const string MicrosoftVSRazorNamespace = $"{nameof(Microsoft)}.{nameof(VisualStudio)}.{nameof(Razor)}";

// Types that will not contribute to fault bucketing. Fully qualified name is
// required in order to match correctly.
private static readonly FrozenSet<string> s_faultIgnoredTypeNames = new string[] {
"Microsoft.AspNetCore.Razor.NullableExtensions"
}.ToFrozenSet();

protected ImmutableArray<TelemetrySession> TelemetrySessions { get; set; }

protected TelemetryReporter(ImmutableArray<TelemetrySession> telemetrySessions = default)
Expand Down Expand Up @@ -163,62 +177,18 @@ public void ReportFault(Exception exception, string? message, params object?[] @
return 0;
});

var (moduleName, methodName) = GetModifiedFaultParameters(exception);
faultEvent.SetFailureParameters(
failureParameter1: moduleName,
failureParameter2: methodName);

Report(faultEvent);
}
catch (Exception)
{
}
}

private static string GetExceptionDetails(Exception exception)
{
const string CodeAnalysisNamespace = nameof(Microsoft) + "." + nameof(CodeAnalysis);
const string AspNetCoreNamespace = nameof(Microsoft) + "." + nameof(AspNetCore);

// Be resilient to failing here. If we can't get a suitable name, just fall back to the standard name we
// used to report.
try
{
// walk up the stack looking for the first call from a type that isn't in the ErrorReporting namespace.
var frames = new StackTrace(exception).GetFrames();

// On the .NET Framework, GetFrames() can return null even though it's not documented as such.
// At least one case here is if the exception's stack trace itself is null.
if (frames != null)
{
foreach (var frame in frames)
{
var method = frame?.GetMethod();
var methodName = method?.Name;
if (methodName is null)
{
continue;
}

var declaringTypeName = method?.DeclaringType?.FullName;
if (declaringTypeName == null)
{
continue;
}

if (!declaringTypeName.StartsWith(CodeAnalysisNamespace) &&
!declaringTypeName.StartsWith(AspNetCoreNamespace))
{
continue;
}

return declaringTypeName + "." + methodName;
}
}
}
catch
{
}

// If we couldn't get a stack, do this
return exception.Message;
}

protected virtual void Report(TelemetryEvent telemetryEvent)
{
try
Expand Down Expand Up @@ -297,4 +267,123 @@ public TelemetryScope TrackLspRequest(string lspMethodName, string languageServe
new("eventscope.languageservername", languageServerName),
new("eventscope.correlationid", correlationId));
}


/// <summary>
/// Returns values that should be set to (failureParameter1, failureParameter2) when reporting a fault.
/// Those values represent the blamed stackframe module and method name.
/// </summary>
internal static (string?, string?) GetModifiedFaultParameters(Exception exception)
{
var frame = FindFirstRazorStackFrame(exception, static (declaringTypeName, _) =>
{
if (s_faultIgnoredTypeNames.Contains(declaringTypeName))
{
return false;
}

return true;
});

var method = frame?.GetMethod();
if (method is null)
{
return (null, null);
}

var moduleName = Path.GetFileNameWithoutExtension(method.Module.Name);
return (moduleName, method.Name);
}

private static string GetExceptionDetails(Exception exception)
{
var frame = FindFirstRazorStackFrame(exception);

if (frame is null)
{
return exception.Message;
}

var method = frame.GetMethod();

// These are checked in FindFirstRazorStackFrame
method.AssumeNotNull();
method.DeclaringType.AssumeNotNull();

var declaringTypeName = method.DeclaringType.FullName;
var methodName = method.Name;

return declaringTypeName + "." + methodName;
}

/// <summary>
/// Finds the first stack frame in exception stack that originates from razor code based on namespace
/// </summary>
/// <param name="exception">The exception to get the stack from</param>
/// <param name="predicate">Optional predicate to filter by declaringTypeName and methodName</param>
/// <returns></returns>
private static StackFrame? FindFirstRazorStackFrame(
Exception exception,
Func<string, string, bool>? predicate = null)
{
// Be resilient to failing here. If we can't get a suitable name, just fall back to the standard name we
// used to report.
try
{
// walk up the stack looking for the first call from a type that isn't in the ErrorReporting namespace.
var frames = new StackTrace(exception).GetFrames();

// On the .NET Framework, GetFrames() can return null even though it's not documented as such.
// At least one case here is if the exception's stack trace itself is null.
if (frames != null)
{
foreach (var frame in frames)
{
if (frame is null)
{
continue;
}

var method = frame.GetMethod();
var methodName = method?.Name;
if (methodName is null)
{
continue;
}

var declaringTypeName = method?.DeclaringType?.FullName;
if (declaringTypeName == null)
{
continue;
}

if (!IsInOwnedNamespace(declaringTypeName))
{
continue;
}

if (predicate is null)
{
return frame;
}

if (predicate(declaringTypeName, methodName))
{
return frame;
}
}
}

return null;
}
catch
{
return null;
}
}

private static bool IsInOwnedNamespace(string declaringTypeName)
=> declaringTypeName.StartsWith(CodeAnalysisNamespace) ||
declaringTypeName.StartsWith(AspNetCoreNamespace) ||
declaringTypeName.StartsWith(MicrosoftVSRazorNamespace);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.VisualStudio.Editor.Razor.Test.Shared;
Expand Down Expand Up @@ -392,4 +394,33 @@ public void ReportFault_InnerMostExceptionIsOperationCanceledException_SkipsFaul
// Assert
Assert.Empty(reporter.Events);
}

[Theory, MemberData(nameof(s_throwFunctions))]
public void GetModifiedFaultParameters_FiltersCorrectly(Func<object> throwAction)
{
try
{
throwAction();
}
catch (Exception ex)
{
var (param1, param2) = TestTelemetryReporter.GetModifiedFaultParameters(ex);

Assert.Equal("Microsoft.VisualStudio.LanguageServices.Razor.Test", param1);
Assert.NotNull(param2);

// Depending on debug/release the stack can contain a constructor or
// a call to this method. We expect one or the other and both
// are valid
#if DEBUG
Assert.StartsWith("<.cctor>", param2);
#else
Assert.Equal("GetModifiedFaultParameters_FiltersCorrectly", param2);
#endif
}
}

public static readonly IEnumerable<object[]> s_throwFunctions = [
[() => ((object?)null).AssumeNotNull()]
];
}
Loading

0 comments on commit af590ea

Please sign in to comment.