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

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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 @@ -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,7 +122,7 @@ 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);
Expand Down Expand Up @@ -749,13 +749,14 @@ 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);

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,12 +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 Action<SyntaxNode>? _testFaultInjector;

private static TraceLog Log
=> EditAndContinueService.AnalysisLog;
Expand Down Expand Up @@ -573,7 +568,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,15 +579,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 @@ -676,7 +671,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 @@ -728,7 +723,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 @@ -819,7 +814,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 +867,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 +6774,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
Original file line number Diff line number Diff line change
Expand Up @@ -413,14 +413,14 @@ await TryGetMatchingSourceTextAsync(sourceText, sourceFilePath, currentDocument:

if (debugInfoReaderProvider == null)
{
EditAndContinueService.Log.Write("Source file of project '{0}' doesn't match output PDB: PDB '{1}' (assembly: '{2}') not found", projectName, compilationOutputs.PdbDisplayPath, compilationOutputs.AssemblyDisplayPath);
EditAndContinueService.Log.Write($"Source file of project '{projectName}' doesn't match output PDB: PDB '{compilationOutputs.PdbDisplayPath}' (assembly: '{compilationOutputs.AssemblyDisplayPath}') not found", LogMessageSeverity.Warning);
}

return debugInfoReaderProvider;
}
catch (Exception e)
{
EditAndContinueService.Log.Write("Source file of project '{0}' doesn't match output PDB: error opening PDB '{1}' (assembly: '{2}'): {3}", projectName, compilationOutputs.PdbDisplayPath, compilationOutputs.AssemblyDisplayPath, e.Message);
EditAndContinueService.Log.Write($"Source file of project '{projectName}' doesn't match output PDB: error opening PDB '{compilationOutputs.PdbDisplayPath}' (assembly: '{compilationOutputs.AssemblyDisplayPath}'): {e.Message}", LogMessageSeverity.Warning);
return null;
}
}
Expand Down Expand Up @@ -453,14 +453,14 @@ private static bool IsMatchingSourceText(SourceText sourceText, ImmutableArray<b
return sourceText;
}

EditAndContinueService.Log.Write("Checksum differs for source file '{0}'", sourceFilePath);
EditAndContinueService.Log.Write($"Checksum differs for source file '{sourceFilePath}'", LogMessageSeverity.Warning);

// does not match:
return null;
}
catch (Exception e)
{
EditAndContinueService.Log.Write("Error calculating checksum for source file '{0}': '{1}'", sourceFilePath, e.Message);
EditAndContinueService.Log.Write($"Error calculating checksum for source file '{sourceFilePath}': '{e.Message}'", LogMessageSeverity.Error);

// unable to determine:
return default;
Expand Down Expand Up @@ -500,22 +500,22 @@ private static bool IsMatchingSourceText(SourceText sourceText, ImmutableArray<b
{
if (!debugInfoReader.TryGetDocumentChecksum(sourceFilePath, out checksum, out var algorithmId))
{
EditAndContinueService.Log.Write("Source '{0}' doesn't match output PDB: no document", sourceFilePath);
EditAndContinueService.Log.Write($"Source '{sourceFilePath}' doesn't match output PDB: no document", LogMessageSeverity.Warning);
return false;
}

algorithm = SourceHashAlgorithms.GetSourceHashAlgorithm(algorithmId);
if (algorithm == SourceHashAlgorithm.None)
{
// This can only happen if the PDB was post-processed by a misbehaving tool.
EditAndContinueService.Log.Write("Source '{0}' doesn't match PDB: unknown checksum alg", sourceFilePath);
EditAndContinueService.Log.Write($"Source '{sourceFilePath}' doesn't match PDB: unknown checksum alg", LogMessageSeverity.Warning);
}

return true;
}
catch (Exception e)
{
EditAndContinueService.Log.Write("Source '{0}' doesn't match output PDB: error reading symbols: {1}", sourceFilePath, e.Message);
EditAndContinueService.Log.Write($"Source '{sourceFilePath}' doesn't match output PDB: error reading symbols: {e.Message}", LogMessageSeverity.Error);
}

// unable to determine
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ private static unsafe bool TryCreateInitialBaseline(
}
catch (Exception e)
{
EditAndContinueService.Log.Write("Failed to create baseline for '{0}': {1}", projectId, e.Message);
EditAndContinueService.Log.Write($"Failed to create baseline for '{projectId.DebugName}': {e.Message}", LogMessageSeverity.Error);

var descriptor = EditAndContinueDiagnosticDescriptors.GetDescriptor(EditAndContinueErrorCode.ErrorReadingFile);
diagnostics = [Diagnostic.Create(descriptor, Location.None, [fileBeingRead, e.Message])];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ public override string ToString()
=> Ordinal.ToString();
}

internal readonly record struct UpdateId(DebuggingSessionId SessionId, int Ordinal);
internal readonly record struct UpdateId(DebuggingSessionId SessionId, int Ordinal)
{
public override string ToString()
=> $"{SessionId}.{Ordinal}";
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ namespace Microsoft.CodeAnalysis.EditAndContinue;
/// Implements core of Edit and Continue orchestration: management of edit sessions and connecting EnC related services.
/// </summary>
[Export(typeof(IEditAndContinueService)), Shared]
internal sealed class EditAndContinueService : IEditAndContinueService
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class EditAndContinueService() : IEditAndContinueService
{
[ExportWorkspaceService(typeof(IEditAndContinueWorkspaceService)), Shared]
[method: ImportingConstructor]
Expand All @@ -48,25 +50,18 @@ private sealed class VoidSessionTracker : IEditAndContinueSessionTracker
internal static readonly TraceLog Log;
internal static readonly TraceLog AnalysisLog;

private Func<Project, CompilationOutputs> _compilationOutputsProvider;
private Func<Project, CompilationOutputs> _compilationOutputsProvider = GetCompilationOutputs;

/// <summary>
/// List of active debugging sessions (small number of simoultaneously active sessions is expected).
/// </summary>
private readonly List<DebuggingSession> _debuggingSessions = [];
private static int s_debuggingSessionId;

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public EditAndContinueService()
{
_compilationOutputsProvider = GetCompilationOutputs;
}

static EditAndContinueService()
{
Log = new(2048, "EnC", "Trace.log");
AnalysisLog = new(1024, "EnC", "Analysis.log");
Log = new(2048, "Trace.log");
AnalysisLog = new(1024, "Analysis.log");

var logDir = GetLogDirectory();
if (logDir != null)
Expand Down Expand Up @@ -171,7 +166,7 @@ public async ValueTask<DebuggingSessionId> StartDebuggingSessionAsync(
_debuggingSessions.Add(session);
}

Log.Write("Session #{0} started.", sessionId.Ordinal);
Log.Write($"Session #{sessionId} started.");
return sessionId;

}
Expand Down Expand Up @@ -200,7 +195,7 @@ public void EndDebuggingSession(DebuggingSessionId sessionId)

debuggingSession.EndSession(out var telemetryData);

Log.Write("Session #{0} ended.", debuggingSession.Id.Ordinal);
Log.Write($"Session #{debuggingSession.Id} ended.");
}

public void BreakStateOrCapabilitiesChanged(DebuggingSessionId sessionId, bool? inBreakState)
Expand Down
Loading
Loading