Skip to content

Commit

Permalink
EnC Reporter
Browse files Browse the repository at this point in the history
  • Loading branch information
tmat committed Jan 21, 2025
1 parent c9c15ab commit 17f647b
Show file tree
Hide file tree
Showing 20 changed files with 239 additions and 124 deletions.
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 @@ -125,7 +125,8 @@ private static async Task<DocumentAnalysisResults> AnalyzeDocumentAsync(
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 @@ -758,7 +759,8 @@ public async Task AnalyzeDocumentAsync_InternalError(bool outOfMemory)
}
};

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 @@ -71,9 +71,6 @@ internal abstract class AbstractEditAndContinueAnalyzer : IEditAndContinueAnalyz
// used by tests to validate correct handlign of unexpected exceptions
private Action<SyntaxNode>? _testFaultInjector;

private static TraceLog Log
=> EditAndContinueService.AnalysisLog;

internal abstract bool ExperimentalFeaturesEnabled(SyntaxTree tree);

/// <summary>
Expand Down Expand Up @@ -511,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 @@ -568,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 '{filePath}'");
log.Write($"Syntax errors found in '{filePath}'");
return DocumentAnalysisResults.SyntaxErrors(newDocument.Id, filePath, [], syntaxError, analysisStopwatch.Elapsed, hasChanges);
}

Expand All @@ -579,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: '{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 '{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 @@ -662,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 @@ -671,7 +668,7 @@ public async Task<DocumentAnalysisResults> AnalyzeDocumentAsync(
}
else
{
Log.Write($"Capabilities required by '{filePath}': {capabilities.GrantedCapabilities}");
log.Write($"Capabilities required by '{filePath}': {capabilities.GrantedCapabilities}");
}

var hasBlockingRudeEdits = diagnostics.HasBlockingRudeEdits();
Expand Down Expand Up @@ -705,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 @@ -723,7 +720,7 @@ static void LogRudeEdits(ArrayBuilder<RudeEditDiagnostic> diagnostics, SourceTex
lineText = null;
}

Log.Write($"Rude edit {diagnostic.Kind}:{diagnostic.SyntaxKind} '{filePath}' line {lineNumber}: '{lineText}'");
log.Write($"Rude edit {diagnostic.Kind}:{diagnostic.SyntaxKind} '{filePath}' line {lineNumber}: '{lineText}'");
}
}
}
Expand Down Expand Up @@ -781,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 @@ -814,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: {oldStatementSpan}", LogMessageSeverity.Warning);
log.Write($"Invalid active statement span: {oldStatementSpan}", LogMessageSeverity.Warning);
continue;
}

Expand Down Expand Up @@ -867,7 +865,7 @@ private void AnalyzeUnchangedActiveMemberBodies(
}
else
{
Log.Write($"Invalid active statement span: {oldStatementSpan}", LogMessageSeverity.Warning);
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
Loading

0 comments on commit 17f647b

Please sign in to comment.