private static async Task <DocumentAnalyzerPerformance> TestDocumentPerformanceAsync(ImmutableDictionary <string, ImmutableArray <DiagnosticAnalyzer> > analyzers, Project project, DocumentId documentId, Options analyzerOptionsInternal, CancellationToken cancellationToken) { if (!analyzers.TryGetValue(project.Language, out var languageAnalyzers)) { languageAnalyzers = ImmutableArray <DiagnosticAnalyzer> .Empty; } Compilation compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var stopwatch = PerformanceTracker.StartNew(); for (int i = 0; i < analyzerOptionsInternal.TestDocumentIterations; i++) { var workspaceAnalyzerOptions = new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution); CompilationWithAnalyzers compilationWithAnalyzers = compilation.WithAnalyzers(languageAnalyzers, new CompilationWithAnalyzersOptions(workspaceAnalyzerOptions, null, analyzerOptionsInternal.RunConcurrent, logAnalyzerExecutionTime: true, reportSuppressedDiagnostics: analyzerOptionsInternal.ReportSuppressedDiagnostics)); SyntaxTree tree = await project.GetDocument(documentId).GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); await compilationWithAnalyzers.GetAnalyzerSyntaxDiagnosticsAsync(tree, cancellationToken).ConfigureAwait(false); await compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(compilation.GetSemanticModel(tree), null, cancellationToken).ConfigureAwait(false); } return(new DocumentAnalyzerPerformance(analyzerOptionsInternal.TestDocumentIterations / stopwatch.Elapsed.TotalSeconds, stopwatch.AllocatedBytes / Math.Max(1, analyzerOptionsInternal.TestDocumentIterations))); }
public async Task RunAsync(Workspace workspace, CancellationToken cancellationToken) { if (!HasRefactorings) { return; } if (!string.IsNullOrEmpty(_options.ProfileRoot)) { ProfileOptimization.StartProfile(nameof(CodeRefactoringRunner)); } var solution = workspace.CurrentSolution; var stopwatch = PerformanceTracker.StartNew(); var updatedSolution = solution; foreach (var project in solution.Projects) { foreach (var document in project.Documents) { var newDocument = await RefactorDocumentAsync(document, cancellationToken).ConfigureAwait(false); if (newDocument is null) { continue; } updatedSolution = updatedSolution.WithDocumentSyntaxRoot(document.Id, await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)); } } if (_options.ApplyChanges) { workspace.TryApplyChanges(updatedSolution); } }
// Also runs per document analysis, used by AnalyzerRunner CLI tool internal async Task RunAllAsync(CancellationToken cancellationToken) { if (!HasAnalyzers) { return; } var solution = _workspace.CurrentSolution; solution = SetOptions(solution); var stopwatch = PerformanceTracker.StartNew(); var analysisResult = await GetAnalysisResultAsync(solution, _analyzers, _options, cancellationToken).ConfigureAwait(false); var allDiagnostics = analysisResult.Where(pair => pair.Value != null).SelectMany(pair => pair.Value.GetAllDiagnostics()).ToImmutableArray(); Console.WriteLine($"Found {allDiagnostics.Length} diagnostics in {stopwatch.GetSummary(preciseMemory: true)}"); WriteTelemetry(analysisResult); if (_options.TestDocuments) { // Make sure we have a compilation for each project foreach (var project in solution.Projects) { if (project.Language != LanguageNames.CSharp && project.Language != LanguageNames.VisualBasic) { continue; } _ = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); } var projectPerformance = new Dictionary <ProjectId, double>(); var documentPerformance = new Dictionary <DocumentId, DocumentAnalyzerPerformance>(); foreach (var projectId in solution.ProjectIds) { var project = solution.GetProject(projectId); if (project.Language != LanguageNames.CSharp && project.Language != LanguageNames.VisualBasic) { continue; } foreach (var documentId in project.DocumentIds) { var document = project.GetDocument(documentId); if (!_options.TestDocumentMatch(document.FilePath)) { continue; } var currentDocumentPerformance = await TestDocumentPerformanceAsync(_analyzers, project, documentId, _options, cancellationToken).ConfigureAwait(false); Console.WriteLine($"{document.FilePath ?? document.Name}: {currentDocumentPerformance.EditsPerSecond:0.00} ({currentDocumentPerformance.AllocatedBytesPerEdit} bytes)"); documentPerformance.Add(documentId, currentDocumentPerformance); } var sumOfDocumentAverages = documentPerformance.Where(x => x.Key.ProjectId == projectId).Sum(x => x.Value.EditsPerSecond); double documentCount = documentPerformance.Where(x => x.Key.ProjectId == projectId).Count(); if (documentCount > 0) { projectPerformance[project.Id] = sumOfDocumentAverages / documentCount; } } var slowestFiles = documentPerformance.OrderBy(pair => pair.Value.EditsPerSecond).GroupBy(pair => pair.Key.ProjectId); Console.WriteLine("Slowest files in each project:"); foreach (var projectGroup in slowestFiles) { Console.WriteLine($" {solution.GetProject(projectGroup.Key).Name}"); foreach (var pair in projectGroup.Take(5)) { var document = solution.GetDocument(pair.Key); Console.WriteLine($" {document.FilePath ?? document.Name}: {pair.Value.EditsPerSecond:0.00} ({pair.Value.AllocatedBytesPerEdit} bytes)"); } } foreach (var projectId in solution.ProjectIds) { if (!projectPerformance.TryGetValue(projectId, out var averageEditsInProject)) { continue; } var project = solution.GetProject(projectId); Console.WriteLine($"{project.Name} ({project.DocumentIds.Count} documents): {averageEditsInProject:0.00} edits per second"); } } foreach (var group in allDiagnostics.GroupBy(diagnostic => diagnostic.Id).OrderBy(diagnosticGroup => diagnosticGroup.Key, StringComparer.OrdinalIgnoreCase)) { Console.WriteLine($" {group.Key}: {group.Count()} instances"); // Print out analyzer diagnostics like AD0001 for analyzer exceptions if (group.Key.StartsWith("AD", StringComparison.Ordinal)) { foreach (var item in group) { Console.WriteLine(item); } } } if (!string.IsNullOrWhiteSpace(_options.LogFileName)) { WriteDiagnosticResults(analysisResult.SelectMany(pair => pair.Value.GetAllDiagnostics().Select(j => Tuple.Create(pair.Key, j))).ToImmutableArray(), _options.LogFileName); } }
public static async Task Main(string[] args) { Options options; try { options = Options.Create(args); } catch (InvalidDataException) { PrintHelp(); return; } CancellationTokenSource cts = new CancellationTokenSource(); Console.CancelKeyPress += (sender, e) => { e.Cancel = true; cts.Cancel(); }; // QueryVisualStudioInstances returns Visual Studio installations on .NET Framework, and .NET Core SDK // installations on .NET Core. We use the one with the most recent version. var msBuildInstance = MSBuildLocator.QueryVisualStudioInstances().OrderByDescending(x => x.Version).First(); #if NETCOREAPP // Since we do not inherit msbuild.deps.json when referencing the SDK copy // of MSBuild and because the SDK no longer ships with version matched assemblies, we // register an assembly loader that will load assemblies from the msbuild path with // equal or higher version numbers than requested. LooseVersionAssemblyLoader.Register(msBuildInstance.MSBuildPath); #endif MSBuildLocator.RegisterInstance(msBuildInstance); var incrementalAnalyzerRunner = new IncrementalAnalyzerRunner(options); var diagnosticAnalyzerRunner = new DiagnosticAnalyzerRunner(options); var codeRefactoringRunner = new CodeRefactoringRunner(options); if (!incrementalAnalyzerRunner.HasAnalyzers && !diagnosticAnalyzerRunner.HasAnalyzers && !codeRefactoringRunner.HasRefactorings) { WriteLine("No analyzers found", ConsoleColor.Red); PrintHelp(); return; } var cancellationToken = cts.Token; if (!string.IsNullOrEmpty(options.ProfileRoot)) { Directory.CreateDirectory(options.ProfileRoot); ProfileOptimization.SetProfileRoot(options.ProfileRoot); } var stopwatch = PerformanceTracker.StartNew(); var properties = new Dictionary <string, string> { #if NETCOREAPP // This property ensures that XAML files will be compiled in the current AppDomain // rather than a separate one. Any tasks isolated in AppDomains or tasks that create // AppDomains will likely not work due to https://github.com/Microsoft/MSBuildLocator/issues/16. { "AlwaysCompileMarkupFilesInSeparateDomain", bool.FalseString }, #endif // Use the latest language version to force the full set of available analyzers to run on the project. { "LangVersion", "latest" }, }; if (!string.IsNullOrEmpty(options.ProfileRoot)) { ProfileOptimization.StartProfile(nameof(MSBuildWorkspace.OpenSolutionAsync)); } using (MSBuildWorkspace workspace = MSBuildWorkspace.Create(properties, AnalyzerRunnerMefHostServices.DefaultServices)) { Solution solution = await workspace.OpenSolutionAsync(options.SolutionPath, progress : null, cancellationToken).ConfigureAwait(false); var projectIds = solution.ProjectIds; foreach (var workspaceDiagnostic in workspace.Diagnostics) { if (workspaceDiagnostic.Kind == WorkspaceDiagnosticKind.Failure) { Console.WriteLine(workspaceDiagnostic.Message); } } foreach (var projectId in projectIds) { solution = solution.WithProjectAnalyzerReferences(projectId, ImmutableArray <AnalyzerReference> .Empty); } Console.WriteLine($"Loaded solution in {stopwatch.GetSummary(preciseMemory: true)}"); if (options.ShowStats) { stopwatch = PerformanceTracker.StartNew(); List <Project> projects = solution.Projects.Where(project => project.Language == LanguageNames.CSharp || project.Language == LanguageNames.VisualBasic).ToList(); Console.WriteLine("Number of projects:\t\t" + projects.Count); Console.WriteLine("Number of documents:\t\t" + projects.Sum(x => x.DocumentIds.Count)); var statistics = GetSolutionStatistics(projects, cancellationToken); Console.WriteLine("Number of syntax nodes:\t\t" + statistics.NumberofNodes); Console.WriteLine("Number of syntax tokens:\t" + statistics.NumberOfTokens); Console.WriteLine("Number of syntax trivia:\t" + statistics.NumberOfTrivia); Console.WriteLine($"Statistics gathered in {stopwatch.GetSummary(preciseMemory: true)}"); } if (options.ShowCompilerDiagnostics) { var projects = solution.Projects.Where(project => project.Language == LanguageNames.CSharp || project.Language == LanguageNames.VisualBasic).ToList(); var diagnosticStatistics = new Dictionary <string, (string description, DiagnosticSeverity severity, int count)>(); foreach (var project in projects) { var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); foreach (var diagnostic in compilation.GetDiagnostics(cancellationToken)) { diagnosticStatistics.TryGetValue(diagnostic.Id, out var existing); var description = existing.description; if (string.IsNullOrEmpty(description)) { description = diagnostic.Descriptor?.Title.ToString(); if (string.IsNullOrEmpty(description)) { description = diagnostic.Descriptor?.MessageFormat.ToString(); } } diagnosticStatistics[diagnostic.Id] = (description, diagnostic.Descriptor.DefaultSeverity, existing.count + 1); } } foreach (var pair in diagnosticStatistics) { Console.WriteLine($" {pair.Value.severity} {pair.Key}: {pair.Value.count} instances ({pair.Value.description})"); } } Console.WriteLine("Pausing 5 seconds before starting analysis..."); await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(false); await incrementalAnalyzerRunner.RunAsync(workspace, cancellationToken).ConfigureAwait(false); await diagnosticAnalyzerRunner.RunAsync(workspace, cancellationToken).ConfigureAwait(false); await codeRefactoringRunner.RunAsync(workspace, cancellationToken).ConfigureAwait(false); } }
public static async Task Main(string[] args) { Options options; try { options = Options.Create(args); } catch (InvalidDataException) { PrintHelp(); return; } var cts = new CancellationTokenSource(); Console.CancelKeyPress += (sender, e) => { e.Cancel = true; cts.Cancel(); }; var cancellationToken = cts.Token; if (!string.IsNullOrEmpty(options.ProfileRoot)) { Directory.CreateDirectory(options.ProfileRoot); ProfileOptimization.SetProfileRoot(options.ProfileRoot); } using var workspace = AnalyzerRunnerHelper.CreateWorkspace(); var incrementalAnalyzerRunner = new IncrementalAnalyzerRunner(workspace, options); var diagnosticAnalyzerRunner = new DiagnosticAnalyzerRunner(workspace, options); var codeRefactoringRunner = new CodeRefactoringRunner(workspace, options); if (!incrementalAnalyzerRunner.HasAnalyzers && !diagnosticAnalyzerRunner.HasAnalyzers && !codeRefactoringRunner.HasRefactorings) { WriteLine("No analyzers found", ConsoleColor.Red); PrintHelp(); return; } var stopwatch = PerformanceTracker.StartNew(); if (!string.IsNullOrEmpty(options.ProfileRoot)) { ProfileOptimization.StartProfile(nameof(MSBuildWorkspace.OpenSolutionAsync)); } await workspace.OpenSolutionAsync(options.SolutionPath, progress : null, cancellationToken).ConfigureAwait(false); foreach (var workspaceDiagnostic in workspace.Diagnostics) { if (workspaceDiagnostic.Kind == WorkspaceDiagnosticKind.Failure) { Console.WriteLine(workspaceDiagnostic.Message); } } Console.WriteLine($"Loaded solution in {stopwatch.GetSummary(preciseMemory: true)}"); if (options.ShowStats) { stopwatch = PerformanceTracker.StartNew(); ShowSolutionStatistics(workspace.CurrentSolution, cancellationToken); Console.WriteLine($"Statistics gathered in {stopwatch.GetSummary(preciseMemory: true)}"); } if (options.ShowCompilerDiagnostics) { await ShowCompilerDiagnosticsAsync(workspace.CurrentSolution, cancellationToken).ConfigureAwait(false); } Console.WriteLine("Pausing 5 seconds before starting analysis..."); await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(false); if (incrementalAnalyzerRunner.HasAnalyzers) { if (!string.IsNullOrEmpty(options.ProfileRoot)) { ProfileOptimization.StartProfile(nameof(Microsoft.CodeAnalysis.SolutionCrawler.IIncrementalAnalyzer)); } await incrementalAnalyzerRunner.RunAsync(cancellationToken).ConfigureAwait(false); } if (diagnosticAnalyzerRunner.HasAnalyzers) { if (!string.IsNullOrEmpty(options.ProfileRoot)) { ProfileOptimization.StartProfile(nameof(DiagnosticAnalyzerRunner)); } await diagnosticAnalyzerRunner.RunAllAsync(cancellationToken).ConfigureAwait(false); } if (codeRefactoringRunner.HasRefactorings) { if (!string.IsNullOrEmpty(options.ProfileRoot)) { ProfileOptimization.StartProfile(nameof(CodeRefactoringRunner)); } await codeRefactoringRunner.RunAsync(cancellationToken).ConfigureAwait(false); } }