diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -1006,10 +1006,23 @@ int CodeCoverageTool::doExport(int argc, const char **argv, CommandLineParserType commandLineParser) { + cl::OptionCategory ExportCategory("Exporting options"); + + cl::opt SkipExpansions("skip-expansions", cl::Optional, + cl::desc("Don't export expanded source regions"), + cl::cat(ExportCategory)); + + cl::opt SkipFunctions("skip-functions", cl::Optional, + cl::desc("Don't export per-function data"), + cl::cat(ExportCategory)); + auto Err = commandLineParser(argc, argv); if (Err) return Err; + ViewOpts.SkipExpansions = SkipExpansions; + ViewOpts.SkipFunctions = SkipFunctions; + if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text && ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) { error("Coverage data can only be exported as textual JSON or an " diff --git a/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/llvm/tools/llvm-cov/CoverageExporterJson.cpp --- a/llvm/tools/llvm-cov/CoverageExporterJson.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterJson.cpp @@ -42,7 +42,14 @@ #include "CoverageExporterJson.h" #include "CoverageReport.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/JSON.h" +#include "llvm/Support/ThreadPool.h" +#include "llvm/Support/Threading.h" +#include +#include +#include /// The semantic version combined as a string. #define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.0" @@ -127,13 +134,15 @@ json::Object renderFile(const coverage::CoverageMapping &Coverage, const std::string &Filename, const FileCoverageSummary &FileReport, - bool ExportSummaryOnly) { + bool ExportSummaryOnly, bool SkipExpansions) { json::Object File({{"filename", Filename}}); if (!ExportSummaryOnly) { // Calculate and render detailed coverage information for given file. auto FileCoverage = Coverage.getCoverageForFile(Filename); File["segments"] = renderFileSegments(FileCoverage, FileReport); - File["expansions"] = renderFileExpansions(FileCoverage, FileReport); + if (!SkipExpansions) { + File["expansions"] = renderFileExpansions(FileCoverage, FileReport); + } } File["summary"] = renderSummary(FileReport); return File; @@ -142,11 +151,23 @@ json::Array renderFiles(const coverage::CoverageMapping &Coverage, ArrayRef SourceFiles, ArrayRef FileReports, - bool ExportSummaryOnly) { + ThreadPool *Pool, bool ExportSummaryOnly, + bool SkipExpansions) { json::Array FileArray; - for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) - FileArray.push_back(renderFile(Coverage, SourceFiles[I], FileReports[I], - ExportSummaryOnly)); + std::mutex FileArrayMutex; + for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) { + auto &SourceFile = SourceFiles[I]; + auto &FileReport = FileReports[I]; + Pool->async([&] { + auto File = renderFile(Coverage, SourceFile, FileReport, + ExportSummaryOnly, SkipExpansions); + { + std::lock_guard Lock(FileArrayMutex); + FileArray.push_back(std::move(File)); + } + }); + } + Pool->wait(); return FileArray; } @@ -173,16 +194,34 @@ renderRoot(SourceFiles); } +static bool compareFileJson(const json::Value &A, const json::Value &B) { + const json::Object *ObjA = A.getAsObject(); + const json::Object *ObjB = B.getAsObject(); + assert(ObjA != nullptr && "Value A was not an Object"); + assert(ObjB != nullptr && "Value B was not an Object"); + const StringRef FilenameA = ObjA->getString("filename").getValue(); + const StringRef FilenameB = ObjB->getString("filename").getValue(); + + return FilenameA.compare(FilenameB) < 0; +} + void CoverageExporterJson::renderRoot(ArrayRef SourceFiles) { FileCoverageSummary Totals = FileCoverageSummary("Totals"); auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals, SourceFiles, Options); - auto Export = - json::Object({{"files", renderFiles(Coverage, SourceFiles, FileReports, - Options.ExportSummaryOnly)}, - {"totals", renderSummary(Totals)}}); + auto NumThreads = Options.NumThreads; + if (NumThreads == 0) { + NumThreads = std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(), + unsigned(SourceFiles.size()))); + } + ThreadPool Pool(NumThreads); + auto Files = renderFiles(Coverage, SourceFiles, FileReports, &Pool, + Options.ExportSummaryOnly, Options.SkipExpansions); + std::sort(Files.begin(), Files.end(), compareFileJson); + auto Export = json::Object( + {{"files", std::move(Files)}, {"totals", renderSummary(Totals)}}); // Skip functions-level information for summary-only export mode. - if (!Options.ExportSummaryOnly) + if (!Options.ExportSummaryOnly && !Options.SkipFunctions) Export["functions"] = renderFunctions(Coverage.getCoveredFunctions()); auto ExportArray = json::Array({std::move(Export)}); diff --git a/llvm/tools/llvm-cov/CoverageViewOptions.h b/llvm/tools/llvm-cov/CoverageViewOptions.h --- a/llvm/tools/llvm-cov/CoverageViewOptions.h +++ b/llvm/tools/llvm-cov/CoverageViewOptions.h @@ -34,6 +34,8 @@ bool ShowRegionSummary; bool ShowInstantiationSummary; bool ExportSummaryOnly; + bool SkipExpansions; + bool SkipFunctions; OutputFormat Format; std::string ShowOutputDirectory; std::vector DemanglerOpts;