Skip to content
Merged
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 @@ -25,6 +25,16 @@ namespace Microsoft.CodeAnalysis.EditAndContinue
[ExportMetadata("UIContext", EditAndContinueUIContext.EncCapableProjectExistsInWorkspaceUIContextString)]
internal sealed class EditAndContinueLanguageService : IManagedHotReloadLanguageService, IEditAndContinueSolutionProvider
{
private sealed class NoSessionException : InvalidOperationException
{
public NoSessionException()
: base("Internal error: no session.")
{
// unique enough HResult to distinguish from other exceptions
HResult = unchecked((int)0x801315087);
}
}

private readonly PdbMatchingSourceTextProvider _sourceTextProvider;
private readonly Lazy<IManagedHotReloadService> _debuggerService;
private readonly IDiagnosticAnalyzerService _diagnosticService;
Expand Down Expand Up @@ -69,11 +79,7 @@ private Solution GetCurrentCompileTimeSolution(Solution? currentDesignTimeSoluti
}

private RemoteDebuggingSessionProxy GetDebuggingSession()
{
var debuggingSession = _debuggingSession;
Contract.ThrowIfNull(debuggingSession);
return debuggingSession;
}
=> _debuggingSession ?? throw new NoSessionException();

private IActiveStatementTrackingService GetActiveStatementTrackingService()
=> WorkspaceProvider.Value.Workspace.Services.GetRequiredService<IActiveStatementTrackingService>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,19 +112,30 @@ private static (Solution, Document) AddDefaultTestProject(
return (solution, solution.Projects.Single().Documents.Single());
}

private static Project AddEmptyTestProject(Solution solution)
{
var projectId = ProjectId.CreateNewId();

return solution.
AddProject(ProjectInfo.Create(
projectId,
VersionStamp.Create(),
"proj",
"proj",
LanguageNames.CSharp,
parseOptions: CSharpParseOptions.Default.WithNoRefSafetyRulesAttribute())
.WithTelemetryId(s_defaultProjectTelemetryId)).GetProject(projectId).
WithMetadataReferences(TargetFrameworkUtil.GetReferences(DefaultTargetFramework));
}

private static Solution AddDefaultTestProject(
Solution solution,
string[] sources,
ISourceGenerator generator = null,
string additionalFileText = null,
(string key, string value)[] analyzerConfig = null)
{
var projectId = ProjectId.CreateNewId();

var project = solution.
AddProject(ProjectInfo.Create(projectId, VersionStamp.Create(), "proj", "proj", LanguageNames.CSharp, parseOptions: CSharpParseOptions.Default.WithNoRefSafetyRulesAttribute()).WithTelemetryId(s_defaultProjectTelemetryId)).GetProject(projectId).
WithMetadataReferences(TargetFrameworkUtil.GetReferences(DefaultTargetFramework));

var project = AddEmptyTestProject(solution);
solution = project.Solution;

if (generator != null)
Expand Down Expand Up @@ -1428,7 +1439,7 @@ public async Task RudeEdits(bool breakMode)
{
"Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=2",
"Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=",
"Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=21|RudeEditSyntaxKind=8910|RudeEditBlocking=True"
"Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=21|RudeEditSyntaxKind=8910|RudeEditBlocking=True|RudeEditProjectId={00000000-AAAA-AAAA-AAAA-111111111111}"
}, _telemetryLog);
}
else
Expand All @@ -1437,7 +1448,7 @@ public async Task RudeEdits(bool breakMode)
{
"Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=1|EmptyHotReloadSessionCount=0",
"Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=",
"Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=21|RudeEditSyntaxKind=8910|RudeEditBlocking=True"
"Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=21|RudeEditSyntaxKind=8910|RudeEditBlocking=True|RudeEditProjectId={00000000-AAAA-AAAA-AAAA-111111111111}"
}, _telemetryLog);
}
}
Expand Down Expand Up @@ -1563,10 +1574,7 @@ public async Task RudeEdits_DocumentOutOfSync(bool breakMode)

using var _ = CreateWorkspace(out var solution, out var service);

var project = solution.
AddProject("test", "test", LanguageNames.CSharp).
AddMetadataReferences(TargetFrameworkUtil.GetReferences(TargetFramework.Mscorlib40));

var project = AddEmptyTestProject(solution);
solution = project.Solution;

// compile with source0:
Expand Down Expand Up @@ -1645,8 +1653,8 @@ public async Task RudeEdits_DocumentOutOfSync(bool breakMode)
{
"Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=2",
"Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=2|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=",
"Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=38|RudeEditSyntaxKind=8875|RudeEditBlocking=True",
"Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=21|RudeEditSyntaxKind=8910|RudeEditBlocking=True"
"Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=38|RudeEditSyntaxKind=8875|RudeEditBlocking=True|RudeEditProjectId={00000000-AAAA-AAAA-AAAA-111111111111}",
"Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=21|RudeEditSyntaxKind=8910|RudeEditBlocking=True|RudeEditProjectId={00000000-AAAA-AAAA-AAAA-111111111111}"
}, _telemetryLog);
}
else
Expand All @@ -1655,8 +1663,8 @@ public async Task RudeEdits_DocumentOutOfSync(bool breakMode)
{
"Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=1|EmptyHotReloadSessionCount=0",
"Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=2|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=",
"Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=38|RudeEditSyntaxKind=8875|RudeEditBlocking=True",
"Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=21|RudeEditSyntaxKind=8910|RudeEditBlocking=True"
"Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=38|RudeEditSyntaxKind=8875|RudeEditBlocking=True|RudeEditProjectId={00000000-AAAA-AAAA-AAAA-111111111111}",
"Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=21|RudeEditSyntaxKind=8910|RudeEditBlocking=True|RudeEditProjectId={00000000-AAAA-AAAA-AAAA-111111111111}"
}, _telemetryLog);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ CommittedSolution.DocumentState.Indeterminate or
return ImmutableArray<Diagnostic>.Empty;
}

EditSession.Telemetry.LogRudeEditDiagnostics(analysis.RudeEditErrors);
EditSession.Telemetry.LogRudeEditDiagnostics(analysis.RudeEditErrors, project.State.Attributes.TelemetryId);

// track the document, so that we can refresh or clean diagnostics at the end of edit session:
EditSession.TrackDocumentWithReportedDiagnostics(document.Id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public static void Log(Data data, Action<FunctionId, LogMessage> log, Func<int>
map["Capabilities"] = (int)editSessionData.Capabilities;

// Ids of all projects whose binaries were successfully updated during the session.
map["ProjectIdsWithAppliedChanges"] = editSessionData.Committed ? editSessionData.ProjectsWithValidDelta.Select(id => new PiiValue(id.ToString("B").ToUpperInvariant())) : "";
map["ProjectIdsWithAppliedChanges"] = editSessionData.Committed ? editSessionData.ProjectsWithValidDelta.Select(ProjectIdToPii) : "";
}));

foreach (var errorId in editSessionData.EmitErrorIds)
Expand All @@ -140,7 +140,7 @@ public static void Log(Data data, Action<FunctionId, LogMessage> log, Func<int>
}));
}

foreach (var (editKind, syntaxKind) in editSessionData.RudeEdits)
foreach (var (editKind, syntaxKind, projectId) in editSessionData.RudeEdits)
{
log(FunctionId.Debugging_EncSession_EditSession_RudeEdit, KeyValueLogMessage.Create(map =>
{
Expand All @@ -150,8 +150,12 @@ public static void Log(Data data, Action<FunctionId, LogMessage> log, Func<int>
map["RudeEditKind"] = editKind;
map["RudeEditSyntaxKind"] = syntaxKind;
map["RudeEditBlocking"] = editSessionData.HadRudeEdits;
map["RudeEditProjectId"] = ProjectIdToPii(projectId);
}));
}

static PiiValue ProjectIdToPii(Guid projectId)
=> new(projectId.ToString("B").ToUpperInvariant());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@ public async ValueTask<SolutionUpdate> EmitSolutionUpdateAsync(Solution solution
if (analysis.RudeEditErrors.Length > 0)
{
documentsWithRudeEdits.Add((analysis.DocumentId, analysis.RudeEditErrors));
Telemetry.LogRudeEditDiagnostics(analysis.RudeEditErrors);
Telemetry.LogRudeEditDiagnostics(analysis.RudeEditErrors, newProject.State.Attributes.TelemetryId);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal sealed class EditSessionTelemetry
{
internal readonly struct Data
{
public readonly ImmutableArray<(ushort EditKind, ushort SyntaxKind)> RudeEdits;
public readonly ImmutableArray<(ushort EditKind, ushort SyntaxKind, Guid projectId)> RudeEdits;
public readonly ImmutableArray<string> EmitErrorIds;
public readonly ImmutableArray<Guid> ProjectsWithValidDelta;
public readonly EditAndContinueCapabilities Capabilities;
Expand Down Expand Up @@ -50,7 +50,7 @@ public Data(EditSessionTelemetry telemetry)
// Limit the number of reported items to limit the size of the telemetry event (max total size is 64K).
private const int MaxReportedProjectIds = 20;

private readonly HashSet<(ushort, ushort)> _rudeEdits = new();
private readonly HashSet<(ushort, ushort, Guid)> _rudeEdits = new();
private readonly HashSet<string> _emitErrorIds = new();
private readonly HashSet<Guid> _projectsWithValidDelta = new();

Expand Down Expand Up @@ -129,13 +129,13 @@ public void LogProjectAnalysisSummary(ProjectAnalysisSummary summary, Guid proje
public void LogProjectAnalysisSummary(ProjectAnalysisSummary summary, Guid projectTelemetryId, ImmutableArray<Diagnostic> emitDiagnostics)
=> LogProjectAnalysisSummary(summary, projectTelemetryId, emitDiagnostics.SelectAsArray(d => d.Severity == DiagnosticSeverity.Error, d => d.Id));

public void LogRudeEditDiagnostics(ImmutableArray<RudeEditDiagnostic> diagnostics)
public void LogRudeEditDiagnostics(ImmutableArray<RudeEditDiagnostic> diagnostics, Guid projectTelemetryId)
{
lock (_guard)
{
foreach (var diagnostic in diagnostics)
{
_rudeEdits.Add(((ushort)diagnostic.Kind, diagnostic.SyntaxKind));
_rudeEdits.Add(((ushort)diagnostic.Kind, diagnostic.SyntaxKind, projectTelemetryId));
}
}
}
Expand Down