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);
            }
        }
示例#4
0
        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);
            }
        }
示例#5
0
        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);
            }
        }