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

Enc logging #76042

Merged
merged 4 commits into from
Jan 23, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.BrokeredServices;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.VisualStudio.Debugger.Contracts.HotReload;
using Roslyn.Utilities;

Expand All @@ -31,13 +29,12 @@ namespace Microsoft.CodeAnalysis.EditAndContinue;
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class EditAndContinueLanguageService(
IServiceBrokerProvider serviceBrokerProvider,
EditAndContinueSessionState sessionState,
Lazy<IHostWorkspaceProvider> workspaceProvider,
Lazy<IManagedHotReloadService> debuggerService,
PdbMatchingSourceTextProvider sourceTextProvider,
IDiagnosticsRefresher diagnosticRefresher,
IAsynchronousOperationListenerProvider listenerProvider) : IManagedHotReloadLanguageService2, IEditAndContinueSolutionProvider
IEditAndContinueLogReporter logReporter,
IDiagnosticsRefresher diagnosticRefresher) : IManagedHotReloadLanguageService2, IEditAndContinueSolutionProvider
{
private sealed class NoSessionException : InvalidOperationException
{
Expand All @@ -49,9 +46,6 @@ public NoSessionException()
}
}

private readonly IAsynchronousOperationListener _asyncListener = listenerProvider.GetListener(FeatureAttribute.EditAndContinue);
private readonly HotReloadLoggerProxy _logger = new(serviceBrokerProvider.ServiceBroker);

private bool _disabled;
private RemoteDebuggingSessionProxy? _debuggingSession;

Expand Down Expand Up @@ -94,11 +88,7 @@ private IActiveStatementTrackingService GetActiveStatementTrackingService()
internal void Disable(Exception e)
{
_disabled = true;

var token = _asyncListener.BeginAsyncOperation(nameof(EditAndContinueLanguageService) + ".LogToOutput");

_ = _logger.LogAsync(new HotReloadLogMessage(HotReloadVerbosity.Diagnostic, e.ToString(), errorLevel: HotReloadDiagnosticErrorLevel.Error), CancellationToken.None).AsTask()
.ReportNonFatalErrorAsync().CompletesAsyncOperation(token);
logReporter.Report(e.ToString(), LogMessageSeverity.Error);
}

private void UpdateApplyChangesDiagnostics(ImmutableArray<DiagnosticData> diagnostics)
Expand Down Expand Up @@ -265,7 +255,7 @@ public async ValueTask UpdateBaselinesAsync(ImmutableArray<string> projectPaths,
}

_committedDesignTimeSolution = currentDesignTimeSolution;
var projectIds = await GetProjectIdsAsync(projectPaths, currentCompileTimeSolution, cancellationToken).ConfigureAwait(false);
var projectIds = GetProjectIds(projectPaths, currentCompileTimeSolution);

try
{
Expand All @@ -281,7 +271,7 @@ public async ValueTask UpdateBaselinesAsync(ImmutableArray<string> projectPaths,
}
}

private async ValueTask<ImmutableArray<ProjectId>> GetProjectIdsAsync(ImmutableArray<string> projectPaths, Solution solution, CancellationToken cancellationToken)
private ImmutableArray<ProjectId> GetProjectIds(ImmutableArray<string> projectPaths, Solution solution)
{
using var _ = ArrayBuilder<ProjectId>.GetInstance(out var projectIds);
foreach (var path in projectPaths)
Expand All @@ -293,11 +283,7 @@ private async ValueTask<ImmutableArray<ProjectId>> GetProjectIdsAsync(ImmutableA
}
else
{
await _logger.LogAsync(new HotReloadLogMessage(
HotReloadVerbosity.Diagnostic,
$"Project with path '{path}' not found in the current solution.",
errorLevel: HotReloadDiagnosticErrorLevel.Warning),
cancellationToken).ConfigureAwait(false);
logReporter.Report($"Project with path '{path}' not found in the current solution.", LogMessageSeverity.Info);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
<ItemGroup>
<Compile Include="..\..\Compilers\Core\Portable\InternalUtilities\IsExternalInit.cs" Link="Utilities\IsExternalInit.cs" />
<Compile Include="..\..\LanguageServer\Microsoft.CodeAnalysis.LanguageServer\BrokeredServices\ServiceBrokerProvider.cs" Link="BrokeredServices\ServiceBrokerProvider.cs" />
<Compile Include="..\..\Workspaces\Remote\ServiceHub\Services\EditAndContinue\EditAndContinueLogReporter.cs" Link="EditAndContinue\EditAndContinueLogReporter.cs" />
<Compile Include="..\..\Workspaces\Remote\ServiceHub\Services\EditAndContinue\HotReloadLoggerProxy.cs" Link="EditAndContinue\HotReloadLoggerProxy.cs" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" Key="$(MoqPublicKey)" LoadsWithinVisualStudio="false" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Differencing;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Collections;
Expand All @@ -26,17 +24,11 @@

namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue;

internal sealed class CSharpEditAndContinueAnalyzer(Action<SyntaxNode>? testFaultInjector = null) : AbstractEditAndContinueAnalyzer(testFaultInjector)
[ExportLanguageService(typeof(IEditAndContinueAnalyzer), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class CSharpEditAndContinueAnalyzer() : AbstractEditAndContinueAnalyzer
{
[ExportLanguageServiceFactory(typeof(IEditAndContinueAnalyzer), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class Factory() : ILanguageServiceFactory
{
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
=> new CSharpEditAndContinueAnalyzer(testFaultInjector: null);
}

#region Syntax Analysis

private enum BlockPart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,11 @@ private static async Task<DocumentAnalysisResults> AnalyzeDocumentAsync(
EditAndContinueCapabilities capabilities = EditAndContinueTestVerifier.Net5RuntimeCapabilities,
ImmutableArray<ActiveStatementLineSpan> newActiveStatementSpans = default)
{
var analyzer = new CSharpEditAndContinueAnalyzer();
var analyzer = oldProject.Services.GetRequiredService<IEditAndContinueAnalyzer>();
var baseActiveStatements = AsyncLazy.Create(activeStatementMap ?? ActiveStatementsMap.Empty);
var lazyCapabilities = AsyncLazy.Create(capabilities);
return await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, newActiveStatementSpans.NullToEmpty(), lazyCapabilities, CancellationToken.None);
var log = new TraceLog("Test");
return await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, newActiveStatementSpans.NullToEmpty(), lazyCapabilities, log, CancellationToken.None);
}

#endregion
Expand Down Expand Up @@ -749,15 +750,17 @@ public async Task AnalyzeDocumentAsync_InternalError(bool outOfMemory)
var baseActiveStatements = AsyncLazy.Create(ActiveStatementsMap.Empty);
var capabilities = AsyncLazy.Create(EditAndContinueTestVerifier.Net5RuntimeCapabilities);

var analyzer = new CSharpEditAndContinueAnalyzer(node =>
var analyzer = Assert.IsType<CSharpEditAndContinueAnalyzer>(oldProject.Services.GetRequiredService<IEditAndContinueAnalyzer>());
analyzer.GetTestAccessor().FaultInjector = node =>
{
if (node is CompilationUnitSyntax)
{
throw outOfMemory ? new OutOfMemoryException() : new NullReferenceException("NullRef!");
}
});
};

var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, [], capabilities, CancellationToken.None);
var log = new TraceLog("Test");
var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, [], capabilities, log, CancellationToken.None);

var expectedDiagnostic = outOfMemory
? $"ENC0089: {string.Format(FeaturesResources.Modifying_source_file_0_requires_restarting_the_application_because_the_file_is_too_big, filePath)}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,13 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.Differencing;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.EditAndContinue.UnitTests;
using Xunit;

namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests;

internal sealed class CSharpEditAndContinueTestVerifier(Action<SyntaxNode>? faultInjector = null) : EditAndContinueTestVerifier
internal sealed class CSharpEditAndContinueTestVerifier(Action<SyntaxNode>? faultInjector = null) : EditAndContinueTestVerifier(faultInjector)
{
private readonly CSharpEditAndContinueAnalyzer _analyzer = new(faultInjector);

public override AbstractEditAndContinueAnalyzer Analyzer => _analyzer;
public override string LanguageName => LanguageNames.CSharp;
public override string ProjectFileExtension => ".csproj";
public override TreeComparer<SyntaxNode> TopSyntaxComparer => SyntaxComparer.TopLevel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.CodeAnalysis.Differencing;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.Contracts.EditAndContinue;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.EditAndContinue.UnitTests;
using Roslyn.Test.Utilities;
using Xunit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using Xunit;
using Microsoft.CodeAnalysis.Host.Mef;
using System.Linq;

namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests;

Expand All @@ -27,11 +29,6 @@ public abstract class EditingTestBase : CSharpTestBase
namespace System.Runtime.CompilerServices { class CreateNewOnMetadataUpdateAttribute : Attribute {} }
";

internal static CSharpEditAndContinueAnalyzer CreateAnalyzer()
{
return new CSharpEditAndContinueAnalyzer(testFaultInjector: null);
}

internal enum MethodKind
{
Regular,
Expand Down Expand Up @@ -184,7 +181,8 @@ internal static Match<SyntaxNode> GetMethodMatch(string src1, string src2, Metho
internal static IEnumerable<KeyValuePair<SyntaxNode, SyntaxNode>> GetMethodMatches(string src1, string src2, MethodKind kind = MethodKind.Regular)
{
var methodMatch = GetMethodMatch(src1, src2, kind);
return EditAndContinueTestVerifier.GetMethodMatches(CreateAnalyzer(), methodMatch);
var analyzer = EditAndContinueTestVerifier.CreateAnalyzer(faultInjector: null, LanguageNames.CSharp);
return EditAndContinueTestVerifier.GetMethodMatches(analyzer, methodMatch);
}

public static MatchingPairs ToMatchingPairs(Match<SyntaxNode> match)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.EditAndContinue.UnitTests;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests;

[UseExportProvider]
public class StatementMatchingTests : EditingTestBase
{
#region Known Matches
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,7 @@ internal abstract class AbstractEditAndContinueAnalyzer : IEditAndContinueAnalyz
SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

// used by tests to validate correct handlign of unexpected exceptions
private readonly Action<SyntaxNode>? _testFaultInjector;

protected AbstractEditAndContinueAnalyzer(Action<SyntaxNode>? testFaultInjector)
{
_testFaultInjector = testFaultInjector;
}

private static TraceLog Log
=> EditAndContinueService.AnalysisLog;
private Action<SyntaxNode>? _testFaultInjector;

internal abstract bool ExperimentalFeaturesEnabled(SyntaxTree tree);

Expand Down Expand Up @@ -516,6 +508,7 @@ public async Task<DocumentAnalysisResults> AnalyzeDocumentAsync(
Document newDocument,
ImmutableArray<ActiveStatementLineSpan> newActiveStatementSpans,
AsyncLazy<EditAndContinueCapabilities> lazyCapabilities,
TraceLog log,
CancellationToken cancellationToken)
{
var filePath = newDocument.FilePath;
Expand Down Expand Up @@ -573,7 +566,7 @@ public async Task<DocumentAnalysisResults> AnalyzeDocumentAsync(
{
// Bail, since we can't do syntax diffing on broken trees (it would not produce useful results anyways).
// If we needed to do so for some reason, we'd need to harden the syntax tree comparers.
Log.Write("Syntax errors found in '{0}'", filePath);
log.Write($"Syntax errors found in '{filePath}'");
return DocumentAnalysisResults.SyntaxErrors(newDocument.Id, filePath, [], syntaxError, analysisStopwatch.Elapsed, hasChanges);
}

Expand All @@ -584,16 +577,15 @@ public async Task<DocumentAnalysisResults> AnalyzeDocumentAsync(
// a) comparing texts is cheaper than diffing trees
// b) we need to ignore errors in unchanged documents

Log.Write("Document unchanged: '{0}'", filePath);
log.Write($"Document unchanged: '{filePath}'");
return DocumentAnalysisResults.Unchanged(newDocument.Id, filePath, analysisStopwatch.Elapsed);
}

// Disallow modification of a file with experimental features enabled.
// These features may not be handled well by the analysis below.
if (ExperimentalFeaturesEnabled(newTree))
{
Log.Write("Experimental features enabled in '{0}'", filePath);

log.Write($"Experimental features enabled in '{filePath}'");
return DocumentAnalysisResults.SyntaxErrors(newDocument.Id, filePath, [new RudeEditDiagnostic(RudeEditKind.ExperimentalFeaturesEnabled, default)], syntaxError: null, analysisStopwatch.Elapsed, hasChanges);
}

Expand Down Expand Up @@ -667,7 +659,7 @@ public async Task<DocumentAnalysisResults> AnalyzeDocumentAsync(

cancellationToken.ThrowIfCancellationRequested();

AnalyzeUnchangedActiveMemberBodies(diagnostics, syntacticEdits.Match, newText, oldActiveStatements, newActiveStatementSpans, newActiveStatements, newExceptionRegions, cancellationToken);
AnalyzeUnchangedActiveMemberBodies(diagnostics, syntacticEdits.Match, newText, oldActiveStatements, newActiveStatementSpans, newActiveStatements, newExceptionRegions, log, cancellationToken);
Debug.Assert(newActiveStatements.All(a => a != null));

if (!diagnostics.IsEmpty)
Expand All @@ -676,7 +668,7 @@ public async Task<DocumentAnalysisResults> AnalyzeDocumentAsync(
}
else
{
Log.Write("Capabilities required by '{0}': {1}", filePath, capabilities.GrantedCapabilities);
log.Write($"Capabilities required by '{filePath}': {capabilities.GrantedCapabilities}");
}

var hasBlockingRudeEdits = diagnostics.HasBlockingRudeEdits();
Expand Down Expand Up @@ -710,7 +702,7 @@ public async Task<DocumentAnalysisResults> AnalyzeDocumentAsync(
return DocumentAnalysisResults.SyntaxErrors(newDocument.Id, filePath, [diagnostic], syntaxError: null, analysisStopwatch.Elapsed, hasChanges);
}

static void LogRudeEdits(ArrayBuilder<RudeEditDiagnostic> diagnostics, SourceText text, string filePath)
void LogRudeEdits(ArrayBuilder<RudeEditDiagnostic> diagnostics, SourceText text, string filePath)
{
foreach (var diagnostic in diagnostics)
{
Expand All @@ -728,7 +720,7 @@ static void LogRudeEdits(ArrayBuilder<RudeEditDiagnostic> diagnostics, SourceTex
lineText = null;
}

Log.Write("Rude edit {0}:{1} '{2}' line {3}: '{4}'", diagnostic.Kind, diagnostic.SyntaxKind, filePath, lineNumber, lineText);
log.Write($"Rude edit {diagnostic.Kind}:{diagnostic.SyntaxKind} '{filePath}' line {lineNumber}: '{lineText}'");
}
}
}
Expand Down Expand Up @@ -786,6 +778,7 @@ private void AnalyzeUnchangedActiveMemberBodies(
ImmutableArray<ActiveStatementLineSpan> newActiveStatementSpans,
[In, Out] ImmutableArray<ActiveStatement>.Builder newActiveStatements,
[In, Out] ImmutableArray<ImmutableArray<SourceFileSpan>>.Builder newExceptionRegions,
TraceLog log,
CancellationToken cancellationToken)
{
Debug.Assert(!newActiveStatementSpans.IsDefault);
Expand Down Expand Up @@ -819,7 +812,7 @@ private void AnalyzeUnchangedActiveMemberBodies(
// Guard against invalid active statement spans (in case PDB was somehow out of sync with the source).
if (oldBody == null || newBody == null)
{
Log.Write("Invalid active statement span: [{0}..{1})", oldStatementSpan.Start, oldStatementSpan.End);
log.Write($"Invalid active statement span: {oldStatementSpan}", LogMessageSeverity.Warning);
continue;
}

Expand Down Expand Up @@ -872,7 +865,7 @@ private void AnalyzeUnchangedActiveMemberBodies(
}
else
{
Log.Write("Invalid active statement span: [{0}..{1})", oldStatementSpan.Start, oldStatementSpan.End);
log.Write($"Invalid active statement span: {oldStatementSpan}", LogMessageSeverity.Warning);
}

// we were not able to determine the active statement location (PDB data might be invalid)
Expand Down Expand Up @@ -6779,19 +6772,23 @@ private static bool HasGetHashCodeSignature(IMethodSymbol method)
internal TestAccessor GetTestAccessor()
=> new(this);

internal readonly struct TestAccessor(AbstractEditAndContinueAnalyzer abstractEditAndContinueAnalyzer)
internal readonly struct TestAccessor(AbstractEditAndContinueAnalyzer analyzer)
{
private readonly AbstractEditAndContinueAnalyzer _abstractEditAndContinueAnalyzer = abstractEditAndContinueAnalyzer;
internal Action<SyntaxNode>? FaultInjector
{
get => analyzer._testFaultInjector = FaultInjector;
set => analyzer._testFaultInjector = value;
}

internal void ReportTopLevelSyntacticRudeEdits(ArrayBuilder<RudeEditDiagnostic> diagnostics, EditScript<SyntaxNode> syntacticEdits, Dictionary<SyntaxNode, EditKind> editMap)
=> _abstractEditAndContinueAnalyzer.ReportTopLevelSyntacticRudeEdits(diagnostics, syntacticEdits, editMap);
=> analyzer.ReportTopLevelSyntacticRudeEdits(diagnostics, syntacticEdits, editMap);

internal DeclarationBodyMap IncludeLambdaBodyMaps(
DeclarationBodyMap bodyMap,
ArrayBuilder<ActiveNode> memberBodyActiveNodes,
ref Dictionary<LambdaBody, LambdaInfo>? lazyActiveOrMatchedLambdas)
{
return _abstractEditAndContinueAnalyzer.IncludeLambdaBodyMaps(bodyMap, memberBodyActiveNodes, ref lazyActiveOrMatchedLambdas);
return analyzer.IncludeLambdaBodyMaps(bodyMap, memberBodyActiveNodes, ref lazyActiveOrMatchedLambdas);
}
}

Expand Down
Loading
Loading