From bbf251e40b9ae08cf256e6813a79d59d27819be1 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Tue, 26 Jul 2016 21:31:47 -0700 Subject: [PATCH] added service hub component and setup project for service hub service hub component is basically thin layer that deals with converting data to pass in to RemoteWorkspace --- NuGet.Config | 1 + Roslyn.sln | 69 ++++++ src/Tools/SignRoslyn/BatchSignData.json | 118 ++++++----- .../OutOfProcCompilerDiagnosticAnalyzer.cs | 171 +++++++++++++++ .../Setup.Next/AssemblyRedirects.cs | 6 +- .../Setup.Next/VisualStudioSetup.Next.csproj | 82 +++++++- ...odeAnalysisService.servicehub.service.json | 9 + src/VisualStudio/Setup.Next/project.json | 2 + .../remoteHostService.servicehub.service.json | 9 + ...HubSnapshotService.servicehub.service.json | 9 + .../Setup.Next/source.extension.vsixmanifest | 4 + .../Diagnostics/DiagnosticArguments.cs | 47 +++++ .../Diagnostics/DiagnosticResultSerializer.cs | 197 ++++++++++++++++++ .../Remote/ServiceHub/PublicAPI.Shipped.txt | 0 .../Remote/ServiceHub/PublicAPI.Unshipped.txt | 0 .../Remote/ServiceHub/ServiceHub.csproj | 74 +++++++ .../Services/CodeAnalysisService.cs | 16 ++ .../CodeAnalysisService_Diagnostics.cs | 80 +++++++ .../ServiceHub/Services/RemoteHostService.cs | 37 ++++ .../Services/ServiceHubJsonRpcServiceBase.cs | 51 +++++ .../Services/ServiceHubServiceBase.cs | 51 +++++ .../Services/ServiceHubSnapshotService.cs | 90 ++++++++ src/Workspaces/Remote/ServiceHub/project.json | 8 + 23 files changed, 1073 insertions(+), 58 deletions(-) create mode 100644 src/VisualStudio/Next/Diagnostics/OutOfProcCompilerDiagnosticAnalyzer.cs create mode 100644 src/VisualStudio/Setup.Next/codeAnalysisService.servicehub.service.json create mode 100644 src/VisualStudio/Setup.Next/remoteHostService.servicehub.service.json create mode 100644 src/VisualStudio/Setup.Next/serviceHubSnapshotService.servicehub.service.json create mode 100644 src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticArguments.cs create mode 100644 src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticResultSerializer.cs create mode 100644 src/Workspaces/Remote/ServiceHub/PublicAPI.Shipped.txt create mode 100644 src/Workspaces/Remote/ServiceHub/PublicAPI.Unshipped.txt create mode 100644 src/Workspaces/Remote/ServiceHub/ServiceHub.csproj create mode 100644 src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService.cs create mode 100644 src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs create mode 100644 src/Workspaces/Remote/ServiceHub/Services/RemoteHostService.cs create mode 100644 src/Workspaces/Remote/ServiceHub/Services/ServiceHubJsonRpcServiceBase.cs create mode 100644 src/Workspaces/Remote/ServiceHub/Services/ServiceHubServiceBase.cs create mode 100644 src/Workspaces/Remote/ServiceHub/Services/ServiceHubSnapshotService.cs create mode 100644 src/Workspaces/Remote/ServiceHub/project.json diff --git a/NuGet.Config b/NuGet.Config index 82934844841..86001b761c0 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -24,6 +24,7 @@ + diff --git a/Roslyn.sln b/Roslyn.sln index d7608d3a828..5040f8acb15 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -374,6 +374,12 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.CodeAnalysis.Meta EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.CodeAnalysis.PooledObjects", "src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.shproj", "{C1930979-C824-496B-A630-70F5369A636F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicesVisualStudio.Next", "src\VisualStudio\Next\ServicesVisualStudio.Next.csproj", "{FE0D4BDD-1C30-488E-A870-854F5B8C5014}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemoteWorkspaces", "src\Workspaces\Remote\Core\RemoteWorkspaces.csproj", "{F822F72A-CC87-4E31-B57D-853F65CBEBF3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceHub", "src\Workspaces\Remote\ServiceHub\ServiceHub.csproj", "{80FDDD00-9393-47F7-8BAF-7E87CE011068}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Compilers\Core\CommandLine\CommandLine.projitems*{06b26dcb-7a12-48ef-ae50-708593abd05f}*SharedItemsImports = 4 @@ -3205,6 +3211,66 @@ Global {3DA711E1-055F-4352-A5E1-F9169C86A20F}.Release|x64.Build.0 = Release|Any CPU {3DA711E1-055F-4352-A5E1-F9169C86A20F}.Release|x86.ActiveCfg = Release|Any CPU {3DA711E1-055F-4352-A5E1-F9169C86A20F}.Release|x86.Build.0 = Release|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Debug|ARM.ActiveCfg = Debug|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Debug|ARM.Build.0 = Debug|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Debug|x64.ActiveCfg = Debug|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Debug|x64.Build.0 = Debug|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Debug|x86.ActiveCfg = Debug|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Debug|x86.Build.0 = Debug|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Release|Any CPU.Build.0 = Release|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Release|ARM.ActiveCfg = Release|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Release|ARM.Build.0 = Release|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Release|x64.ActiveCfg = Release|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Release|x64.Build.0 = Release|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Release|x86.ActiveCfg = Release|Any CPU + {FE0D4BDD-1C30-488E-A870-854F5B8C5014}.Release|x86.Build.0 = Release|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Debug|ARM.ActiveCfg = Debug|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Debug|ARM.Build.0 = Debug|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Debug|x64.ActiveCfg = Debug|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Debug|x64.Build.0 = Debug|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Debug|x86.ActiveCfg = Debug|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Debug|x86.Build.0 = Debug|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Release|Any CPU.Build.0 = Release|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Release|ARM.ActiveCfg = Release|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Release|ARM.Build.0 = Release|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Release|x64.ActiveCfg = Release|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Release|x64.Build.0 = Release|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Release|x86.ActiveCfg = Release|Any CPU + {F822F72A-CC87-4E31-B57D-853F65CBEBF3}.Release|x86.Build.0 = Release|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Debug|ARM.ActiveCfg = Debug|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Debug|ARM.Build.0 = Debug|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Debug|x64.ActiveCfg = Debug|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Debug|x64.Build.0 = Debug|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Debug|x86.ActiveCfg = Debug|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Debug|x86.Build.0 = Debug|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Release|Any CPU.Build.0 = Release|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Release|ARM.ActiveCfg = Release|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Release|ARM.Build.0 = Release|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Release|x64.ActiveCfg = Release|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Release|x64.Build.0 = Release|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Release|x86.ActiveCfg = Release|Any CPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -3376,5 +3442,8 @@ Global {3DA711E1-055F-4352-A5E1-F9169C86A20F} = {FD0FAF5F-1DED-485C-99FA-84B97F3A8EEC} {D73ADF7D-2C1C-42AE-B2AB-EDC9497E4B71} = {C2D1346B-9665-4150-B644-075CF1636BAA} {C1930979-C824-496B-A630-70F5369A636F} = {C2D1346B-9665-4150-B644-075CF1636BAA} + {FE0D4BDD-1C30-488E-A870-854F5B8C5014} = {8DBA5174-B0AA-4561-82B1-A46607697753} + {F822F72A-CC87-4E31-B57D-853F65CBEBF3} = {55A62CFA-1155-46F1-ADF3-BEEE51B58AB5} + {80FDDD00-9393-47F7-8BAF-7E87CE011068} = {55A62CFA-1155-46F1-ADF3-BEEE51B58AB5} EndGlobalSection EndGlobal diff --git a/src/Tools/SignRoslyn/BatchSignData.json b/src/Tools/SignRoslyn/BatchSignData.json index 74111b43081..0222bdbe680 100644 --- a/src/Tools/SignRoslyn/BatchSignData.json +++ b/src/Tools/SignRoslyn/BatchSignData.json @@ -3,62 +3,65 @@ { "certificate": "Microsoft402", "strongName": "MsSharedLib72", - "values": [ - "csc.exe", - "csccore\\csc.exe", - "csi.exe", - "csicore\\csi.exe", - "InteractiveHost.exe", - "Microsoft.Build.Tasks.CodeAnalysis.dll", - "Microsoft.CodeAnalysis.CSharp.dll", - "Microsoft.CodeAnalysis.CSharp.EditorFeatures.dll", - "Microsoft.CodeAnalysis.CSharp.Features.dll", - "Microsoft.CodeAnalysis.CSharp.InteractiveEditorFeatures.dll", - "Microsoft.CodeAnalysis.CSharp.Scripting.dll", - "Microsoft.CodeAnalysis.CSharp.Workspaces.dll", - "Microsoft.CodeAnalysis.dll", - "Microsoft.CodeAnalysis.EditorFeatures.dll", - "Microsoft.CodeAnalysis.EditorFeatures.Next.dll", - "Microsoft.CodeAnalysis.EditorFeatures.Text.dll", - "Microsoft.CodeAnalysis.Features.dll", - "Microsoft.CodeAnalysis.InteractiveEditorFeatures.dll", - "Microsoft.CodeAnalysis.InteractiveFeatures.dll", - "Microsoft.CodeAnalysis.Scripting.dll", - "Microsoft.CodeAnalysis.VisualBasic.dll", - "Microsoft.CodeAnalysis.VisualBasic.EditorFeatures.dll", - "Microsoft.CodeAnalysis.VisualBasic.Features.dll", - "Microsoft.CodeAnalysis.VisualBasic.InteractiveEditorFeatures.dll", - "Microsoft.CodeAnalysis.VisualBasic.Scripting.dll", - "Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll", - "Microsoft.CodeAnalysis.Workspaces.Desktop.dll", - "Microsoft.CodeAnalysis.Workspaces.dll", - "Microsoft.DiaSymReader.PortablePdb.dll", - "Microsoft.VisualStudio.CSharp.Repl.dll", - "Microsoft.VisualStudio.InteractiveServices.dll", - "Microsoft.VisualStudio.InteractiveWindow.dll", - "Microsoft.VisualStudio.LanguageServices.CSharp.dll", - "Microsoft.VisualStudio.LanguageServices.dll", - "Microsoft.VisualStudio.LanguageServices.Implementation.dll", - "Microsoft.VisualStudio.LanguageServices.SolutionExplorer.dll", - "Microsoft.VisualStudio.LanguageServices.VisualBasic.dll", - "Microsoft.VisualStudio.VisualBasic.Repl.dll", - "Microsoft.VisualStudio.VsInteractiveWindow.dll", - "Pdb2Xml.exe", - "Roslyn.Compilers.Extension.dll", - "Roslyn.Hosting.Diagnostics.dll", - "Roslyn.VisualStudio.DiagnosticsWindow.dll", - "Roslyn.VisualStudio.InteractiveComponents.dll", - "Roslyn.VisualStudio.Setup.Interactive.dll", - "SDK\\Roslyn.SyntaxVisualizer.DgmlHelper.dll", - "SDK\\Roslyn.SyntaxVisualizer.Control.dll", - "SDK\\Roslyn.SyntaxVisualizer.Extension.dll", - "SDK\\Roslyn.Templates.dll", - "vbc.exe", - "vbccore\\vbc.exe", - "VBCSCompiler.exe", - "vbi.exe", - "vbicore\\vbi.exe" - ] + "values": [ + "csc.exe", + "csccore\\csc.exe", + "csi.exe", + "csicore\\csi.exe", + "InteractiveHost.exe", + "Microsoft.Build.Tasks.CodeAnalysis.dll", + "Microsoft.CodeAnalysis.CSharp.dll", + "Microsoft.CodeAnalysis.CSharp.EditorFeatures.dll", + "Microsoft.CodeAnalysis.CSharp.Features.dll", + "Microsoft.CodeAnalysis.CSharp.InteractiveEditorFeatures.dll", + "Microsoft.CodeAnalysis.CSharp.Scripting.dll", + "Microsoft.CodeAnalysis.CSharp.Workspaces.dll", + "Microsoft.CodeAnalysis.dll", + "Microsoft.CodeAnalysis.EditorFeatures.dll", + "Microsoft.CodeAnalysis.EditorFeatures.Next.dll", + "Microsoft.CodeAnalysis.EditorFeatures.Text.dll", + "Microsoft.CodeAnalysis.Features.dll", + "Microsoft.CodeAnalysis.InteractiveEditorFeatures.dll", + "Microsoft.CodeAnalysis.InteractiveFeatures.dll", + "Microsoft.CodeAnalysis.Remote.ServiceHub.dll", + "Microsoft.CodeAnalysis.Remote.Workspaces.dll", + "Microsoft.CodeAnalysis.Scripting.dll", + "Microsoft.CodeAnalysis.VisualBasic.dll", + "Microsoft.CodeAnalysis.VisualBasic.EditorFeatures.dll", + "Microsoft.CodeAnalysis.VisualBasic.Features.dll", + "Microsoft.CodeAnalysis.VisualBasic.InteractiveEditorFeatures.dll", + "Microsoft.CodeAnalysis.VisualBasic.Scripting.dll", + "Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll", + "Microsoft.CodeAnalysis.Workspaces.Desktop.dll", + "Microsoft.CodeAnalysis.Workspaces.dll", + "Microsoft.DiaSymReader.PortablePdb.dll", + "Microsoft.VisualStudio.CSharp.Repl.dll", + "Microsoft.VisualStudio.InteractiveServices.dll", + "Microsoft.VisualStudio.InteractiveWindow.dll", + "Microsoft.VisualStudio.LanguageServices.CSharp.dll", + "Microsoft.VisualStudio.LanguageServices.dll", + "Microsoft.VisualStudio.LanguageServices.Implementation.dll", + "Microsoft.VisualStudio.LanguageServices.Next.dll", + "Microsoft.VisualStudio.LanguageServices.SolutionExplorer.dll", + "Microsoft.VisualStudio.LanguageServices.VisualBasic.dll", + "Microsoft.VisualStudio.VisualBasic.Repl.dll", + "Microsoft.VisualStudio.VsInteractiveWindow.dll", + "Pdb2Xml.exe", + "Roslyn.Compilers.Extension.dll", + "Roslyn.Hosting.Diagnostics.dll", + "Roslyn.VisualStudio.DiagnosticsWindow.dll", + "Roslyn.VisualStudio.InteractiveComponents.dll", + "Roslyn.VisualStudio.Setup.Interactive.dll", + "SDK\\Roslyn.SyntaxVisualizer.DgmlHelper.dll", + "SDK\\Roslyn.SyntaxVisualizer.Control.dll", + "SDK\\Roslyn.SyntaxVisualizer.Extension.dll", + "SDK\\Roslyn.Templates.dll", + "vbc.exe", + "vbccore\\vbc.exe", + "VBCSCompiler.exe", + "vbi.exe", + "vbicore\\vbi.exe" + ] }, { "certificate": "WindowsPhone623", @@ -108,6 +111,9 @@ "Microsoft.DiaSymReader.dll", "Microsoft.DiaSymReader.Native.amd64.dll", "Microsoft.DiaSymReader.Native.x86.dll", + "Newtonsoft.Json.dll", + "StreamJsonRpc.dll", + "StreamJsonRpc.resources.dll", "System.Reflection.Metadata.dll", "System.Collections.Immutable.dll", "System.Diagnostics.StackTrace.dll", diff --git a/src/VisualStudio/Next/Diagnostics/OutOfProcCompilerDiagnosticAnalyzer.cs b/src/VisualStudio/Next/Diagnostics/OutOfProcCompilerDiagnosticAnalyzer.cs new file mode 100644 index 00000000000..d086dc5e073 --- /dev/null +++ b/src/VisualStudio/Next/Diagnostics/OutOfProcCompilerDiagnosticAnalyzer.cs @@ -0,0 +1,171 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Diagnostics.EngineV2; +using Microsoft.CodeAnalysis.Diagnostics.Telemetry; +using Microsoft.CodeAnalysis.Execution; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Remote.Diagnostics; +using Microsoft.CodeAnalysis.Workspaces.Diagnostics; +using Microsoft.VisualStudio.LanguageServices.Implementation.Remote; +using Roslyn.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.Diagnostics +{ + [ExportWorkspaceService(typeof(ICompilerDiagnosticAnalyzer), layer: ServiceLayer.Host), Shared] + internal class OutOfProcCompilerDiagnosticAnalyzer : ICompilerDiagnosticAnalyzer + { + private static readonly ICompilerDiagnosticAnalyzer _inProcAnalyzer = new InProcCompilerDiagnosticAnalyzer(); + + private readonly IDiagnosticAnalyzerService _analyzerService; + private readonly AbstractHostDiagnosticUpdateSource _hostDiagnosticUpdateSource; + + // TODO: solution snapshot tracking for current solution should be its own service + private ChecksumScope _lastSnapshot; + + [ImportingConstructor] + public OutOfProcCompilerDiagnosticAnalyzer( + IDiagnosticAnalyzerService analyzerService, + AbstractHostDiagnosticUpdateSource hostDiagnosticUpdateSource) + { + _analyzerService = analyzerService; + _hostDiagnosticUpdateSource = hostDiagnosticUpdateSource; + } + + public async Task> AnalyzeAsync(CompilationWithAnalyzers analyzerDriver, Project project, CancellationToken cancellationToken) + { + var remoteHostClient = await project.Solution.Workspace.Services.GetService().GetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + if (remoteHostClient == null) + { + // remote host is not running. this can happen if remote host is disabled. + return await AnalyzeInProcAsync(analyzerDriver, project, cancellationToken).ConfigureAwait(false); + } + + // TODO: later, make sure we can run all analyzer on remote host. + // for now, we will check whether built in analyzer can run on remote host and only those run on remote host. + var inProcResultTask = AnalyzeInProcAsync(CreateAnalyzerDriver(analyzerDriver, a => a.MustRunInProc()), project, cancellationToken); + var outOfProcResultTask = AnalyzeOutOfProcAsync(remoteHostClient, analyzerDriver, project, cancellationToken); + + // run them concurrently in vs and remote host + await Task.WhenAll(inProcResultTask, outOfProcResultTask).ConfigureAwait(false); + + // make sure things are not cancelled + cancellationToken.ThrowIfCancellationRequested(); + + // merge 2 results + return DiagnosticAnalysisResultMap.Create( + inProcResultTask.Result.AnalysisResult.AddRange(outOfProcResultTask.Result.AnalysisResult), + inProcResultTask.Result.TelemetryInfo.AddRange(outOfProcResultTask.Result.TelemetryInfo)); + } + + private async Task> AnalyzeInProcAsync(CompilationWithAnalyzers analyzerDriver, Project project, CancellationToken cancellationToken) + { + return await _inProcAnalyzer.AnalyzeAsync(analyzerDriver, project, cancellationToken).ConfigureAwait(false); + } + + private async Task> AnalyzeOutOfProcAsync( + RemoteHostClient client, CompilationWithAnalyzers analyzerDriver, Project project, CancellationToken cancellationToken) + { + var solution = project.Solution; + + var snapshotService = solution.Workspace.Services.GetService(); + + // TODO: incremental build of solution snapshot should be its own service + await UpdateLastSolutionSnapshotAsync(snapshotService, solution).ConfigureAwait(false); + + // TODO: this should be moved out + var hostChecksums = GetHostAnalyzerReferences(snapshotService, _analyzerService.GetHostAnalyzerReferences(), cancellationToken); + var analyzerMap = CreateAnalyzerMap(analyzerDriver.Analyzers.Where(a => !a.MustRunInProc())); + if (analyzerMap.Count == 0) + { + return DiagnosticAnalysisResultMap.Create(ImmutableDictionary.Empty, ImmutableDictionary.Empty); + } + + // TODO: send telemetry on session + using (var session = await client.CreateCodeAnalysisServiceSessionAsync(solution, cancellationToken).ConfigureAwait(false)) + { + var argument = new DiagnosticArguments( + analyzerDriver.AnalysisOptions.ReportSuppressedDiagnostics, + analyzerDriver.AnalysisOptions.LogAnalyzerExecutionTime, + project.Id, hostChecksums, analyzerMap.Keys.ToArray()); + + var result = await session.InvokeAsync( + WellKnownServiceHubServices.CodeAnalysisService_CalculateDiagnosticsAsync, + new object[] { argument }, + (s, c) => GetCompilerAnalysisResultAsync(s, analyzerMap, project, c)).ConfigureAwait(false); + + ReportAnalyzerExceptions(project, result.Exceptions); + + return result; + } + } + + private async Task UpdateLastSolutionSnapshotAsync(ISolutionChecksumService snapshotService, Solution solution) + { + // TODO: actual incremental build of solution snapshot should be its own service + // this is needed to make sure we incrementally update solution checksums. otherwise, we will always create from + // scratch which can be quite expansive for big solution + var lastSnapshot = _lastSnapshot; + _lastSnapshot = await snapshotService.CreateChecksumAsync(solution, CancellationToken.None).ConfigureAwait(false); + lastSnapshot?.Dispose(); + } + + private CompilationWithAnalyzers CreateAnalyzerDriver(CompilationWithAnalyzers analyzerDriver, Func predicate) + { + var analyzers = analyzerDriver.Analyzers.Where(predicate).ToImmutableArray(); + return analyzerDriver.Compilation.WithAnalyzers(analyzers, analyzerDriver.AnalysisOptions); + } + + private async Task> GetCompilerAnalysisResultAsync(Stream stream, Dictionary analyzerMap, Project project, CancellationToken cancellationToken) + { + // handling of cancellation and exception + var version = await DiagnosticIncrementalAnalyzer.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false); + + using (var reader = new ObjectReader(stream)) + { + return DiagnosticResultSerializer.Deserialize(reader, analyzerMap, project, version, cancellationToken); + } + } + + private void ReportAnalyzerExceptions(Project project, ImmutableDictionary> exceptions) + { + foreach (var kv in exceptions) + { + var analyzer = kv.Key; + foreach (var diagnostic in kv.Value) + { + _hostDiagnosticUpdateSource.ReportAnalyzerDiagnostic(analyzer, diagnostic, project); + } + } + } + + private ImmutableArray GetHostAnalyzerReferences(ISolutionChecksumService snapshotService, IEnumerable references, CancellationToken cancellationToken) + { + // TODO: cache this to somewhere + var builder = ImmutableArray.CreateBuilder(); + foreach (var reference in references) + { + var asset = snapshotService.GetGlobalAsset(reference, cancellationToken); + builder.Add(asset.Checksum.ToArray()); + } + + return builder.ToImmutable(); + } + + private Dictionary CreateAnalyzerMap(IEnumerable analyzers) + { + // TODO: this needs to be cached. we can have 300+ analyzers + return analyzers.ToDictionary(a => a.GetAnalyzerIdAndVersion().Item1, a => a); + } + } +} diff --git a/src/VisualStudio/Setup.Next/AssemblyRedirects.cs b/src/VisualStudio/Setup.Next/AssemblyRedirects.cs index f25c23bffe5..8bd91ccc7f0 100644 --- a/src/VisualStudio/Setup.Next/AssemblyRedirects.cs +++ b/src/VisualStudio/Setup.Next/AssemblyRedirects.cs @@ -11,4 +11,8 @@ PublicKeyToken = "31BF3856AD364E35", OldVersionLowerBound = "14.0.0.0", OldVersionUpperBound = "14.9.9.9", - NewVersion = "15.0.0.0")] \ No newline at end of file + NewVersion = "15.0.0.0")] + +[assembly: ProvideCodeBase(CodeBase = @"$PackageFolder$\StreamJsonRpc.dll")] +[assembly: ProvideCodeBase(CodeBase = @"$PackageFolder$\Microsoft.ServiceHub.Client.dll")] +[assembly: ProvideCodeBase(CodeBase = @"$PackageFolder$\Newtonsoft.Json.dll")] \ No newline at end of file diff --git a/src/VisualStudio/Setup.Next/VisualStudioSetup.Next.csproj b/src/VisualStudio/Setup.Next/VisualStudioSetup.Next.csproj index 3728183830e..6bd0ea47aa6 100644 --- a/src/VisualStudio/Setup.Next/VisualStudioSetup.Next.csproj +++ b/src/VisualStudio/Setup.Next/VisualStudioSetup.Next.csproj @@ -24,6 +24,24 @@ false + + {1ee8cad3-55f9-4d91-96b2-084641da9a6c} + CodeAnalysis + BuiltProjectOutputGroup%3b + true + + + {b501a547-c911-4a05-ac6e-274a50dff30e} + CSharpCodeAnalysis + BuiltProjectOutputGroup%3b + true + + + {2523d0e6-df32-4a3e-8ae0-a19bffae2ef6} + BasicCodeAnalysis + BuiltProjectOutputGroup%3b + true + {8da861d8-0cce-4334-b6c0-01a846c881ec} VisualStudio @@ -35,12 +53,62 @@ BuiltProjectOutputGroup%3b DebugSymbolsProjectOutputGroup%3b + + {2e87fa96-50bb-4607-8676-46521599f998} + Workspaces.Desktop + BuiltProjectOutputGroup%3b + true + + + {5f8d2414-064a-4b3a-9b42-8e2a04246be5} + Workspaces + BuiltProjectOutputGroup%3b + true + + + {21b239d0-d144-430f-a394-c066d58ee267} + CSharpWorkspace + BuiltProjectOutputGroup%3b + true + + + {f822f72a-cc87-4e31-b57d-853f65cbebf3} + RemoteWorkspaces + BuiltProjectOutputGroup%3b + true + + + {80fddd00-9393-47f7-8baf-7e87ce011068} + ServiceHub + BuiltProjectOutputGroup%3b + true + + + {57ca988d-f010-4bf2-9a2e-07d6dcd2ff2c} + BasicWorkspace + BuiltProjectOutputGroup%3b + true + + + {fe0d4bdd-1c30-488e-a870-854f5b8c5014} + ServicesVisualStudio.Next + BuiltProjectOutputGroup%3b + true + VisualStudioSetup false False + + + + + + + + AnyCPU @@ -53,7 +121,19 @@ /rootsuffix RoslynDev /log + + PreserveNewest + true + + + PreserveNewest + true + + + PreserveNewest + true + Designer @@ -67,4 +147,4 @@ - + \ No newline at end of file diff --git a/src/VisualStudio/Setup.Next/codeAnalysisService.servicehub.service.json b/src/VisualStudio/Setup.Next/codeAnalysisService.servicehub.service.json new file mode 100644 index 00000000000..27d5cef6a81 --- /dev/null +++ b/src/VisualStudio/Setup.Next/codeAnalysisService.servicehub.service.json @@ -0,0 +1,9 @@ +{ + "$schema": "../../../schemas/servicehub.service.schema.json", + "host": "desktopClr.x86", + "hostId": "RoslynCodeAnalysisService32", + "entryPoint": { + "assemblyPath": "Microsoft.CodeAnalysis.Remote.ServiceHub.dll", + "fullClassName": "Microsoft.CodeAnalysis.Remote.CodeAnalysisService" + } +} \ No newline at end of file diff --git a/src/VisualStudio/Setup.Next/project.json b/src/VisualStudio/Setup.Next/project.json index 0b16291820b..edb027acd43 100644 --- a/src/VisualStudio/Setup.Next/project.json +++ b/src/VisualStudio/Setup.Next/project.json @@ -1,5 +1,7 @@ { "dependencies": { + "StreamJsonRpc": "0.14.3-alpha", + "Newtonsoft.Json": "6.0.6" }, "frameworks": { "net46": { } diff --git a/src/VisualStudio/Setup.Next/remoteHostService.servicehub.service.json b/src/VisualStudio/Setup.Next/remoteHostService.servicehub.service.json new file mode 100644 index 00000000000..2aee3fe44f7 --- /dev/null +++ b/src/VisualStudio/Setup.Next/remoteHostService.servicehub.service.json @@ -0,0 +1,9 @@ +{ + "$schema": "../../../schemas/servicehub.service.schema.json", + "host": "desktopClr.x86", + "hostId": "RoslynCodeAnalysisService32", + "entryPoint": { + "assemblyPath": "Microsoft.CodeAnalysis.Remote.ServiceHub.dll", + "fullClassName": "Microsoft.CodeAnalysis.Remote.RemoteHostService" + } +} \ No newline at end of file diff --git a/src/VisualStudio/Setup.Next/serviceHubSnapshotService.servicehub.service.json b/src/VisualStudio/Setup.Next/serviceHubSnapshotService.servicehub.service.json new file mode 100644 index 00000000000..401220eb250 --- /dev/null +++ b/src/VisualStudio/Setup.Next/serviceHubSnapshotService.servicehub.service.json @@ -0,0 +1,9 @@ +{ + "$schema": "../../../schemas/servicehub.service.schema.json", + "host": "desktopClr.x86", + "hostId": "RoslynCodeAnalysisService32", + "entryPoint": { + "assemblyPath": "Microsoft.CodeAnalysis.Remote.ServiceHub.dll", + "fullClassName": "Microsoft.CodeAnalysis.Remote.ServiceHubSnapshotService" + } +} \ No newline at end of file diff --git a/src/VisualStudio/Setup.Next/source.extension.vsixmanifest b/src/VisualStudio/Setup.Next/source.extension.vsixmanifest index 02a77efbd27..5c375b4515b 100644 --- a/src/VisualStudio/Setup.Next/source.extension.vsixmanifest +++ b/src/VisualStudio/Setup.Next/source.extension.vsixmanifest @@ -17,5 +17,9 @@ + + + + diff --git a/src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticArguments.cs b/src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticArguments.cs new file mode 100644 index 00000000000..e3c805b5078 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticArguments.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis.Execution; + +namespace Microsoft.CodeAnalysis.Remote.Diagnostics +{ + /// + /// helper type to package diagnostic arguments to pass around between remote hosts + /// + internal class DiagnosticArguments + { + public bool ReportSuppressedDiagnostics; + public bool LogAnalyzerExecutionTime; + public Guid ProjectIdGuid; + public string ProjectIdDebugName; + public byte[][] HostAnalyzerChecksumsByteArray; + public string[] AnalyzerIds; + + public DiagnosticArguments() + { + } + + public DiagnosticArguments( + bool reportSuppressedDiagnostics, + bool logAnalyzerExecutionTime, + ProjectId projectId, + ImmutableArray hostAnalyzerChecksums, + string[] analyzerIds) + { + ReportSuppressedDiagnostics = reportSuppressedDiagnostics; + LogAnalyzerExecutionTime = logAnalyzerExecutionTime; + + ProjectIdGuid = projectId.Id; + ProjectIdDebugName = projectId.DebugName; + + HostAnalyzerChecksumsByteArray = hostAnalyzerChecksums.ToArray(); + AnalyzerIds = analyzerIds; + } + + public ProjectId GetProjectId() => ProjectId.CreateFromSerialized(ProjectIdGuid, ProjectIdDebugName); + public IEnumerable GetHostAnalyzerChecksums() => HostAnalyzerChecksumsByteArray.Select(b => new Checksum(b)); + } +} \ No newline at end of file diff --git a/src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticResultSerializer.cs b/src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticResultSerializer.cs new file mode 100644 index 00000000000..3c613ece847 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Diagnostics/DiagnosticResultSerializer.cs @@ -0,0 +1,197 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Diagnostics.Telemetry; +using Microsoft.CodeAnalysis.Execution; +using Microsoft.CodeAnalysis.Workspaces.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Remote.Diagnostics +{ + internal static class DiagnosticResultSerializer + { + public static void Serialize(ObjectWriter writer, DiagnosticAnalysisResultMap result, CancellationToken cancellationToken) + { + var diagnosticSerializer = new DiagnosticDataSerializer(VersionStamp.Default, VersionStamp.Default); + + var analysisResult = result.AnalysisResult; + + writer.WriteInt32(analysisResult.Count); + foreach (var kv in analysisResult) + { + writer.WriteString(kv.Key); + + Serialize(writer, diagnosticSerializer, kv.Value.SyntaxLocals, cancellationToken); + Serialize(writer, diagnosticSerializer, kv.Value.SemanticLocals, cancellationToken); + Serialize(writer, diagnosticSerializer, kv.Value.NonLocals, cancellationToken); + + diagnosticSerializer.WriteTo(writer, kv.Value.Others, cancellationToken); + } + + var telemetryInfo = result.TelemetryInfo; + + writer.WriteInt32(telemetryInfo.Count); + foreach (var kv in telemetryInfo) + { + writer.WriteString(kv.Key); + Serialize(writer, kv.Value, cancellationToken); + } + + var exceptions = result.Exceptions; + + writer.WriteInt32(exceptions.Count); + foreach (var kv in exceptions) + { + writer.WriteString(kv.Key); + diagnosticSerializer.WriteTo(writer, kv.Value, cancellationToken); + } + } + + public static DiagnosticAnalysisResultMap Deserialize( + ObjectReader reader, IDictionary analyzerMap, Project project, VersionStamp version, CancellationToken cancellationToken) + { + var diagnosticDataSerializer = new DiagnosticDataSerializer(VersionStamp.Default, VersionStamp.Default); + + var analysisMap = ImmutableDictionary.CreateBuilder(); + + var analysisCount = reader.ReadInt32(); + for (var i = 0; i < analysisCount; i++) + { + var analyzer = analyzerMap[reader.ReadString()]; + + var syntaxLocalMap = Deserialize(reader, diagnosticDataSerializer, project, cancellationToken); + var semanticLocalMap = Deserialize(reader, diagnosticDataSerializer, project, cancellationToken); + var nonLocalMap = Deserialize(reader, diagnosticDataSerializer, project, cancellationToken); + + var others = diagnosticDataSerializer.ReadFrom(reader, project, cancellationToken); + + var analysisResult = new DiagnosticAnalysisResult( + project.Id, version, + syntaxLocalMap, semanticLocalMap, nonLocalMap, others, + documentIds: null, fromBuild: false); + + analysisMap.Add(analyzer, analysisResult); + } + + var telemetryMap = ImmutableDictionary.CreateBuilder(); + + var telemetryCount = reader.ReadInt32(); + for (var i = 0; i < telemetryCount; i++) + { + var analyzer = analyzerMap[reader.ReadString()]; + var telemetryInfo = Deserialize(reader, cancellationToken); + + telemetryMap.Add(analyzer, telemetryInfo); + } + + var exceptionMap = ImmutableDictionary.CreateBuilder>(); + + var exceptionCount = reader.ReadInt32(); + for (var i = 0; i < exceptionCount; i++) + { + var analyzer = analyzerMap[reader.ReadString()]; + var exceptions = diagnosticDataSerializer.ReadFrom(reader, project, cancellationToken); + + exceptionMap.Add(analyzer, exceptions); + } + + return DiagnosticAnalysisResultMap.Create(analysisMap.ToImmutable(), telemetryMap.ToImmutable(), exceptionMap.ToImmutable()); + } + + private static void Serialize( + ObjectWriter writer, + DiagnosticDataSerializer serializer, + ImmutableDictionary> diagnostics, + CancellationToken cancellationToken) + { + writer.WriteInt32(diagnostics.Count); + foreach (var kv in diagnostics) + { + Serializer.Serialize(kv.Key, writer, cancellationToken); + serializer.WriteTo(writer, kv.Value, cancellationToken); + } + } + + private static ImmutableDictionary> Deserialize( + ObjectReader reader, + DiagnosticDataSerializer serializer, + Project project, + CancellationToken cancellationToken) + { + var count = reader.ReadInt32(); + var map = ImmutableDictionary.CreateBuilder>(); + for (var i = 0; i < count; i++) + { + var documentId = Serializer.DeserializeDocumentId(reader, cancellationToken); + var diagnostics = serializer.ReadFrom(reader, project.GetDocument(documentId), cancellationToken); + + map.Add(documentId, diagnostics); + } + + return map.ToImmutable(); + } + + private static void Serialize(ObjectWriter writer, AnalyzerTelemetryInfo telemetryInfo, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + writer.WriteInt32(telemetryInfo.CompilationStartActionsCount); + writer.WriteInt32(telemetryInfo.CompilationEndActionsCount); + writer.WriteInt32(telemetryInfo.CompilationActionsCount); + writer.WriteInt32(telemetryInfo.SyntaxTreeActionsCount); + writer.WriteInt32(telemetryInfo.SemanticModelActionsCount); + writer.WriteInt32(telemetryInfo.SymbolActionsCount); + writer.WriteInt32(telemetryInfo.SyntaxNodeActionsCount); + writer.WriteInt32(telemetryInfo.CodeBlockStartActionsCount); + writer.WriteInt32(telemetryInfo.CodeBlockEndActionsCount); + writer.WriteInt32(telemetryInfo.CodeBlockActionsCount); + writer.WriteInt32(telemetryInfo.OperationActionsCount); + writer.WriteInt32(telemetryInfo.OperationBlockActionsCount); + writer.WriteInt32(telemetryInfo.OperationBlockStartActionsCount); + writer.WriteInt32(telemetryInfo.OperationBlockEndActionsCount); + writer.WriteInt64(telemetryInfo.ExecutionTime.Ticks); + } + + private static AnalyzerTelemetryInfo Deserialize(ObjectReader reader, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var compilationStartActionsCount = reader.ReadInt32(); + var compilationEndActionsCount = reader.ReadInt32(); + var compilationActionsCount = reader.ReadInt32(); + var syntaxTreeActionsCount = reader.ReadInt32(); + var semanticModelActionsCount = reader.ReadInt32(); + var symbolActionsCount = reader.ReadInt32(); + var syntaxNodeActionsCount = reader.ReadInt32(); + var codeBlockStartActionsCount = reader.ReadInt32(); + var codeBlockEndActionsCount = reader.ReadInt32(); + var codeBlockActionsCount = reader.ReadInt32(); + var operationActionsCount = reader.ReadInt32(); + var operationBlockActionsCount = reader.ReadInt32(); + var operationBlockStartActionsCount = reader.ReadInt32(); + var operationBlockEndActionsCount = reader.ReadInt32(); + var executionTime = new TimeSpan(reader.ReadInt64()); + + return new AnalyzerTelemetryInfo( + compilationStartActionsCount, + compilationEndActionsCount, + compilationActionsCount, + syntaxTreeActionsCount, + semanticModelActionsCount, + symbolActionsCount, + syntaxNodeActionsCount, + codeBlockStartActionsCount, + codeBlockEndActionsCount, + codeBlockActionsCount, + operationActionsCount, + operationBlockStartActionsCount, + operationBlockEndActionsCount, + operationBlockActionsCount, + executionTime); + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/PublicAPI.Shipped.txt b/src/Workspaces/Remote/ServiceHub/PublicAPI.Shipped.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/Workspaces/Remote/ServiceHub/PublicAPI.Unshipped.txt b/src/Workspaces/Remote/ServiceHub/PublicAPI.Unshipped.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj new file mode 100644 index 00000000000..335eca8d1cd --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj @@ -0,0 +1,74 @@ + + + + CSharp + + + + + + Debug + AnyCPU + {80FDDD00-9393-47F7-8BAF-7E87CE011068} + Library + Microsoft.CodeAnalysis.Remote + Microsoft.CodeAnalysis.Remote.ServiceHub + v4.6 + false + + + + {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C} + CodeAnalysis + + + {DCDA908D-EF5E-494B-ADDC-C26F5FD610CA} + Immutable + + + {2e87fa96-50bb-4607-8676-46521599f998} + Workspaces.Desktop + + + {5F8D2414-064A-4B3A-9B42-8E2A04246BE5} + Workspaces + + + {f822f72a-cc87-4e31-b57d-853f65cbebf3} + RemoteWorkspaces + + + + true + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService.cs new file mode 100644 index 00000000000..037311868b2 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; + +namespace Microsoft.CodeAnalysis.Remote +{ + // root level service for all Roslyn services + internal partial class CodeAnalysisService : ServiceHubJsonRpcServiceBase + { + public CodeAnalysisService(Stream stream, IServiceProvider serviceProvider) : + base(stream, serviceProvider) + { + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs new file mode 100644 index 00000000000..7199f7b5191 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_Diagnostics.cs @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Execution; +using Microsoft.CodeAnalysis.Remote.Diagnostics; +using Microsoft.CodeAnalysis.Workspaces.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Remote +{ + // root level service for all Roslyn services + internal partial class CodeAnalysisService + { + /// + /// This is top level entry point for diagnostic calculation from client (VS). + /// + /// This will be called by ServiceHub/JsonRpc framework + /// + public async Task CalculateDiagnosticsAsync(DiagnosticArguments arguments, byte[] solutionChecksum, string streamName) + { + try + { + // entry point for diagnostic service + var solution = await RoslynServices.SolutionService.GetSolutionAsync(new Checksum(solutionChecksum), CancellationToken).ConfigureAwait(false); + var projectId = arguments.GetProjectId(); + var analyzers = await GetHostAnalyzerReferences(arguments.GetHostAnalyzerChecksums()).ConfigureAwait(false); + + var result = await (new DiagnosticComputer(solution.GetProject(projectId))).GetDiagnosticsAsync( + analyzers, arguments.AnalyzerIds, arguments.ReportSuppressedDiagnostics, arguments.LogAnalyzerExecutionTime, CancellationToken).ConfigureAwait(false); + + await SerializeDiagnosticResultAsync(streamName, result).ConfigureAwait(false); + } + catch (IOException) + { + // stream to send over result has closed before we + // had chance to check cancellation + } + catch (OperationCanceledException) + { + // rpc connection has closed. + // this can happen if client side cancelled the + // operation + } + } + + private async Task> GetHostAnalyzerReferences(IEnumerable checksums) + { + var analyzers = new List(); + foreach (var checksum in checksums) + { + analyzers.Add(await RoslynServices.AssetService.GetAssetAsync(checksum, CancellationToken).ConfigureAwait(false)); + } + + return analyzers; + } + + private async Task SerializeDiagnosticResultAsync(string streamName, DiagnosticAnalysisResultMap result) + { + using (var stream = new ClientDirectStream(streamName)) + { + await stream.ConnectAsync(CancellationToken).ConfigureAwait(false); + + using (var writer = new ObjectWriter(stream)) + { + DiagnosticResultSerializer.Serialize(writer, result, CancellationToken); + } + + await stream.FlushAsync(CancellationToken).ConfigureAwait(false); + + // TODO: think of a way this is not needed + // wait for the other side to finish reading data I sent over + stream.WaitForServer(); + } + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/RemoteHostService.cs b/src/Workspaces/Remote/ServiceHub/Services/RemoteHostService.cs new file mode 100644 index 00000000000..c00fb48be2a --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/RemoteHostService.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Threading; + +namespace Microsoft.CodeAnalysis.Remote +{ + /// + /// Service that client will connect to to make service hub alive even when there is + /// no other people calling service hub. + /// + /// basically, this is used to manage lifetime of the service hub. + /// + internal class RemoteHostService : ServiceHubJsonRpcServiceBase + { + private string _host; + + public RemoteHostService(Stream stream, IServiceProvider serviceProvider) : + base(stream, serviceProvider) + { + // this service provide a way for client to make sure remote host is alive + } + + public string Connect(string host) + { + var existing = Interlocked.CompareExchange(ref _host, host, null); + + if (existing != null && existing != host) + { + LogError($"{host} is given for {existing}"); + } + + return _host; + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/ServiceHubJsonRpcServiceBase.cs b/src/Workspaces/Remote/ServiceHub/Services/ServiceHubJsonRpcServiceBase.cs new file mode 100644 index 00000000000..25bf1c33a30 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/ServiceHubJsonRpcServiceBase.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using StreamJsonRpc; +using System; +using System.IO; +using System.Threading; + +namespace Microsoft.CodeAnalysis.Remote +{ + internal abstract class ServiceHubJsonRpcServiceBase : ServiceHubServiceBase + { + private readonly CancellationTokenSource _source; + + protected readonly JsonRpc Rpc; + protected readonly CancellationToken CancellationToken; + + public ServiceHubJsonRpcServiceBase(Stream stream, IServiceProvider serviceProvider) : base(stream, serviceProvider) + { + _source = new CancellationTokenSource(); + CancellationToken = _source.Token; + + Rpc = JsonRpc.Attach(stream, this); + Rpc.Disconnected += OnRpcDisconnected; + } + + protected virtual void OnDisconnected(JsonRpcDisconnectedEventArgs e) + { + // do nothing + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + Rpc.Dispose(); + } + + private void OnRpcDisconnected(object sender, JsonRpcDisconnectedEventArgs e) + { + // raise cancellation + _source.Cancel(); + + OnDisconnected(e); + + if (e.Reason != DisconnectedReason.Disposed) + { + LogError($"Client stream disconnected unexpectedly: {e.Exception?.GetType().Name} {e.Exception?.Message}"); + } + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/ServiceHubServiceBase.cs b/src/Workspaces/Remote/ServiceHub/Services/ServiceHubServiceBase.cs new file mode 100644 index 00000000000..ae67767f35c --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/ServiceHubServiceBase.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; + +namespace Microsoft.CodeAnalysis.Remote +{ + // TODO: all service hub service should be extract to interface so that it can support multiple hosts. + // right now, tightly coupled to service hub + internal abstract class ServiceHubServiceBase : IDisposable + { + private static int s_instanceId = 0; + + protected readonly int InstanceId = 0; + protected readonly TraceSource Logger; + protected readonly Stream Stream; + + protected ServiceHubServiceBase(Stream stream, IServiceProvider serviceProvider) + { + InstanceId = Interlocked.Add(ref s_instanceId, 1); + + Logger = (TraceSource)serviceProvider.GetService(typeof(TraceSource)); + Logger.TraceInformation($"{DebugInstanceString} Service instance created"); + + Stream = stream; + } + + protected string DebugInstanceString => $"{GetType().ToString()} ({InstanceId})"; + + protected virtual void Dispose(bool disposing) + { + // do nothing here + } + + protected void LogError(string message) + { + Logger.TraceEvent(TraceEventType.Error, 0, $"{DebugInstanceString} : " + message); + } + + public void Dispose() + { + Stream.Dispose(); + + Dispose(false); + + Logger.TraceInformation($"{DebugInstanceString} Service instance disposed"); + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/ServiceHubSnapshotService.cs b/src/Workspaces/Remote/ServiceHub/Services/ServiceHubSnapshotService.cs new file mode 100644 index 00000000000..3f89824b03a --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/ServiceHubSnapshotService.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Execution; +using Roslyn.Utilities; +using StreamJsonRpc; + +namespace Microsoft.CodeAnalysis.Remote +{ + /// + /// Snapshot service in service hub side. + /// + /// this service will be used to move over snapshot data from client to service hub + /// + internal class ServiceHubSnapshotService : ServiceHubJsonRpcServiceBase + { + private readonly AssetSource _source; + + public ServiceHubSnapshotService(Stream stream, IServiceProvider serviceProvider) : + base(stream, serviceProvider) + { + _source = new ServiceHubAssetSource(Rpc, Logger, CancellationToken); + } + + protected override void OnDisconnected(JsonRpcDisconnectedEventArgs e) + { + _source.Done(); + } + + private class ServiceHubAssetSource : AssetSource + { + private readonly JsonRpc _rpc; + private readonly TraceSource _logger; + private readonly CancellationToken _assetChannelCancellationToken; + + public ServiceHubAssetSource(JsonRpc rpc, TraceSource logger, CancellationToken assetChannelCancellationToken) : + base() + { + _rpc = rpc; + _logger = logger; + _assetChannelCancellationToken = assetChannelCancellationToken; + } + + public override async Task RequestAssetAsync(int serviceId, Checksum checksum, CancellationToken callerCancellationToken) + { + // it should succeed as long as matching VS is alive + // TODO: add logging mechanism using Logger + + // this can be called in two ways. + // 1. Connection to get asset is closed (the asset source we were using is disconnected - _assetChannelCancellationToken) + // if this asset source's channel is closed, service will move to next asset source to get the asset as long as callerCancellationToken + // is not cancelled + // + // 2. Request to required this asset has cancelled. (callerCancellationToken) + using (var mergedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(_assetChannelCancellationToken, callerCancellationToken)) + { + await _rpc.InvokeAsync(WellKnownServiceHubServices.AssetService_RequestAssetAsync, + new object[] { serviceId, checksum.ToArray() }, + (s, c) => ReadAssetAsync(s, _logger, serviceId, checksum, c), mergedCancellationToken.Token).ConfigureAwait(false); + } + } + + private static Task ReadAssetAsync( + Stream stream, TraceSource logger, int serviceId, Checksum checksum, CancellationToken cancellationToken) + { + using (var reader = new ObjectReader(stream)) + { + var responseServiceId = reader.ReadInt32(); + Contract.ThrowIfFalse(serviceId == responseServiceId); + + var responseChecksum = new Checksum(reader.ReadArray()); + Contract.ThrowIfFalse(checksum == responseChecksum); + + var kind = reader.ReadString(); + + // in service hub, cancellation means simply closed stream + var @object = RoslynServices.AssetService.Deserialize(kind, reader, cancellationToken); + + RoslynServices.AssetService.Set(checksum, @object); + + return SpecializedTasks.EmptyTask; + } + } + } + } +} \ No newline at end of file diff --git a/src/Workspaces/Remote/ServiceHub/project.json b/src/Workspaces/Remote/ServiceHub/project.json new file mode 100644 index 00000000000..8b811231339 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/project.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "StreamJsonRpc": "0.14.3-alpha" + }, + "frameworks": { + "net46": {} + } +} -- GitLab