Index: llvm/trunk/docs/CommandGuide/llvm-cov.rst =================================================================== --- llvm/trunk/docs/CommandGuide/llvm-cov.rst +++ llvm/trunk/docs/CommandGuide/llvm-cov.rst @@ -374,9 +374,15 @@ DESCRIPTION ^^^^^^^^^^^ -The :program:`llvm-cov export` command exports regions, functions, expansions, -and summaries of the coverage of the binaries *BIN*,... using the profile data -*PROFILE* as JSON. It can optionally be filtered to only export the coverage +The :program:`llvm-cov export` command exports coverage data of the binaries +*BIN*,... using the profile data *PROFILE* in either JSON or lcov trace file +format. + +When exporting JSON, the regions, functions, expansions, and summaries of the +coverage data will be exported. When exporting an lcov trace file, the +line-based coverage and summaries will be exported. + +The exported data can optionally be filtered to only export the coverage for the files listed in *SOURCES*. For information on compiling programs for coverage and generating profile data, @@ -392,12 +398,18 @@ universal binary or to use an architecture that does not match a non-universal binary. +.. option:: -format= + + Use the specified output format. The supported formats are: "text" (JSON), + "lcov". + .. option:: -summary-only Export only summary information for each file in the coverage data. This mode will not export coverage information for smaller units such as individual - functions or regions. The result will be the same as produced by :program: - `llvm-cov report` command, but presented in JSON format rather than text. + functions or regions. The result will contain the same information as produced + by the :program:`llvm-cov report` command, but presented in JSON or lcov + format rather than text. .. option:: -ignore-filename-regex= Index: llvm/trunk/docs/ReleaseNotes.rst =================================================================== --- llvm/trunk/docs/ReleaseNotes.rst +++ llvm/trunk/docs/ReleaseNotes.rst @@ -40,7 +40,8 @@ functionality, or simply have a lot to talk about), see the `NOTE` below for adding a new subsection. -* Note.. +* The **llvm-cov** tool can now export lcov trace files using the + `-format=lcov` option of the `export` command. .. NOTE If you would like to document a larger change, then you can add a Index: llvm/trunk/test/tools/llvm-cov/showLineExecutionCounts-lcov.test =================================================================== --- llvm/trunk/test/tools/llvm-cov/showLineExecutionCounts-lcov.test +++ llvm/trunk/test/tools/llvm-cov/showLineExecutionCounts-lcov.test @@ -0,0 +1,38 @@ +// FULL: SF:{{.*}}showLineExecutionCounts.cpp +// FULL: FN:6,main +// FULL: FNDA:161,main +// FULL: FNF:1 +// FULL: FNH:1 +int main() { // FULL: DA:[[@LINE]],161 + int x = 0; // FULL: DA:[[@LINE]],161 + // FULL: DA:[[@LINE]],161 + if (x) { // FULL: DA:[[@LINE]],161 + x = 0; // FULL: DA:[[@LINE]],0 + } else { // FULL: DA:[[@LINE]],161 + x = 1; // FULL: DA:[[@LINE]],161 + } // FULL: DA:[[@LINE]],161 + // FULL: DA:[[@LINE]],161 + for (int i = 0; i < 100; ++i) { // FULL: DA:[[@LINE]],16261 + x = 1; // FULL: DA:[[@LINE]],16100 + } // FULL: DA:[[@LINE]],16100 + // FULL: DA:[[@LINE]],161 + x = x < 10 ? x + 1 : x - 1; // FULL: DA:[[@LINE]],161 + x = x > 10 ? // FULL: DA:[[@LINE]],161 + x - 1: // FULL: DA:[[@LINE]],0 + x + 1; // FULL: DA:[[@LINE]],161 + // FULL: DA:[[@LINE]],161 + return 0; // FULL: DA:[[@LINE]],161 +} // FULL: DA:[[@LINE]],161 +// FULL: LF:20 +// FULL: LH:18 +// FULL: end_of_record +// RUN: llvm-profdata merge %S/Inputs/lineExecutionCounts.proftext -o %t.profdata +// RUN: llvm-cov export -format=lcov %S/Inputs/lineExecutionCounts.covmapping -instr-profile %t.profdata %s | FileCheck -check-prefixes=FULL %s + +// RUN: llvm-cov export -format=lcov -summary-only %S/Inputs/lineExecutionCounts.covmapping -instr-profile %t.profdata %s | FileCheck -check-prefixes=SUMMARYONLY %s +// SUMMARYONLY: SF:{{.*}}showLineExecutionCounts.cpp +// SUMMARYONLY: FNF:1 +// SUMMARYONLY: FNH:1 +// SUMMARYONLY: LF:20 +// SUMMARYONLY: LH:18 +// SUMMARYONLY: end_of_record Index: llvm/trunk/tools/llvm-cov/CMakeLists.txt =================================================================== --- llvm/trunk/tools/llvm-cov/CMakeLists.txt +++ llvm/trunk/tools/llvm-cov/CMakeLists.txt @@ -5,6 +5,7 @@ gcov.cpp CodeCoverage.cpp CoverageExporterJson.cpp + CoverageExporterLcov.cpp CoverageFilters.cpp CoverageReport.cpp CoverageSummaryInfo.cpp Index: llvm/trunk/tools/llvm-cov/CodeCoverage.cpp =================================================================== --- llvm/trunk/tools/llvm-cov/CodeCoverage.cpp +++ llvm/trunk/tools/llvm-cov/CodeCoverage.cpp @@ -14,6 +14,7 @@ //===----------------------------------------------------------------------===// #include "CoverageExporterJson.h" +#include "CoverageExporterLcov.h" #include "CoverageFilters.h" #include "CoverageReport.h" #include "CoverageSummaryInfo.h" @@ -566,7 +567,9 @@ cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text", "Text output"), clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html", - "HTML output")), + "HTML output"), + clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov", + "lcov tracefile output")), cl::init(CoverageViewOptions::OutputFormat::Text)); cl::opt PathRemap( @@ -674,6 +677,11 @@ errs() << "Color output cannot be disabled when generating html.\n"; ViewOpts.Colors = true; break; + case CoverageViewOptions::OutputFormat::Lcov: + if (UseColor == cl::BOU_TRUE) + errs() << "Color output cannot be enabled when generating lcov.\n"; + ViewOpts.Colors = false; + break; } // If path-equivalence was given and is a comma seperated pair then set @@ -833,6 +841,11 @@ if (Err) return Err; + if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) { + error("Lcov format should be used with 'llvm-cov export'."); + return 1; + } + ViewOpts.ShowLineNumbers = true; ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || !ShowRegions || ShowBestLineRegionsCounts; @@ -964,6 +977,9 @@ if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) { error("HTML output for summary reports is not yet supported."); return 1; + } else if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) { + error("Lcov format should be used with 'llvm-cov export'."); + return 1; } auto Coverage = load(); @@ -995,8 +1011,10 @@ if (Err) return Err; - if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text) { - error("Coverage data can only be exported as textual JSON."); + if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text && + ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) { + error("Coverage data can only be exported as textual JSON or an " + "lcov tracefile."); return 1; } @@ -1006,12 +1024,29 @@ return 1; } - auto Exporter = CoverageExporterJson(*Coverage.get(), ViewOpts, outs()); + std::unique_ptr Exporter; + + switch (ViewOpts.Format) { + case CoverageViewOptions::OutputFormat::Text: + Exporter = llvm::make_unique(*Coverage.get(), + ViewOpts, outs()); + break; + case CoverageViewOptions::OutputFormat::HTML: + // Unreachable because we should have gracefully terminated with an error + // above. + llvm_unreachable("Export in HTML is not supported!"); + case CoverageViewOptions::OutputFormat::Lcov: + Exporter = llvm::make_unique(*Coverage.get(), + ViewOpts, outs()); + break; + default: + llvm_unreachable("Unknown coverage output format!"); + } if (SourceFiles.empty()) - Exporter.renderRoot(IgnoreFilenameFilters); + Exporter->renderRoot(IgnoreFilenameFilters); else - Exporter.renderRoot(SourceFiles); + Exporter->renderRoot(SourceFiles); return 0; } Index: llvm/trunk/tools/llvm-cov/CoverageExporterLcov.h =================================================================== --- llvm/trunk/tools/llvm-cov/CoverageExporterLcov.h +++ llvm/trunk/tools/llvm-cov/CoverageExporterLcov.h @@ -0,0 +1,36 @@ +//===- CoverageExporterLcov.h - Code coverage lcov exporter ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements a code coverage exporter for lcov trace file format. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_COVERAGEEXPORTERLCOV_H +#define LLVM_COV_COVERAGEEXPORTERLCOV_H + +#include "CoverageExporter.h" + +namespace llvm { + +class CoverageExporterLcov : public CoverageExporter { +public: + CoverageExporterLcov(const coverage::CoverageMapping &CoverageMapping, + const CoverageViewOptions &Options, raw_ostream &OS) + : CoverageExporter(CoverageMapping, Options, OS) {} + + /// Render the CoverageMapping object. + void renderRoot(const CoverageFilters &IgnoreFilters) override; + + /// Render the CoverageMapping object for specified source files. + void renderRoot(ArrayRef SourceFiles) override; +}; + +} // end namespace llvm + +#endif // LLVM_COV_COVERAGEEXPORTERLCOV_H Index: llvm/trunk/tools/llvm-cov/CoverageExporterLcov.cpp =================================================================== --- llvm/trunk/tools/llvm-cov/CoverageExporterLcov.cpp +++ llvm/trunk/tools/llvm-cov/CoverageExporterLcov.cpp @@ -0,0 +1,125 @@ +//===- CoverageExporterLcov.cpp - Code coverage export --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements export of code coverage data to lcov trace file format. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// +// The trace file code coverage export follows the following format (see also +// https://linux.die.net/man/1/geninfo). Each quoted string appears on its own +// line; the indentation shown here is only for documentation purposes. +// +// - for each source file: +// - "SF:" +// - for each function: +// - "FN:," +// - for each function: +// - "FNDA:," +// - "FNF:" +// - "FNH:" +// - for each instrumented line: +// - "DA:,[,] +// - "LH:" +// - "LF:" +// - "end_of_record" +// +// If the user is exporting summary information only, then the FN, FNDA, and DA +// lines will not be present. +// +//===----------------------------------------------------------------------===// + +#include "CoverageExporterLcov.h" +#include "CoverageReport.h" + +using namespace llvm; + +namespace { + +void renderFunctionSummary(raw_ostream &OS, + const FileCoverageSummary &Summary) { + OS << "FNF:" << Summary.FunctionCoverage.getNumFunctions() << '\n' + << "FNH:" << Summary.FunctionCoverage.getExecuted() << '\n'; +} + +void renderFunctions( + raw_ostream &OS, + const iterator_range &Functions) { + for (const auto &F : Functions) { + auto StartLine = F.CountedRegions.front().LineStart; + OS << "FN:" << StartLine << ',' << F.Name << '\n'; + } + for (const auto &F : Functions) + OS << "FNDA:" << F.ExecutionCount << ',' << F.Name << '\n'; +} + +void renderLineExecutionCounts(raw_ostream &OS, + const coverage::CoverageData &FileCoverage) { + coverage::LineCoverageIterator LCI{FileCoverage, 1}; + coverage::LineCoverageIterator LCIEnd = LCI.getEnd(); + for (; LCI != LCIEnd; ++LCI) { + const coverage::LineCoverageStats &LCS = *LCI; + if (LCS.isMapped()) { + OS << "DA:" << LCS.getLine() << ',' << LCS.getExecutionCount() << '\n'; + } + } +} + +void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) { + OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n' + << "LH:" << Summary.LineCoverage.getCovered() << '\n'; +} + +void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage, + const std::string &Filename, + const FileCoverageSummary &FileReport, bool ExportSummaryOnly) { + OS << "SF:" << Filename << '\n'; + + if (!ExportSummaryOnly) { + renderFunctions(OS, Coverage.getCoveredFunctions()); + } + renderFunctionSummary(OS, FileReport); + + if (!ExportSummaryOnly) { + // Calculate and render detailed coverage information for given file. + auto FileCoverage = Coverage.getCoverageForFile(Filename); + renderLineExecutionCounts(OS, FileCoverage); + } + renderLineSummary(OS, FileReport); + + OS << "end_of_record\n"; +} + +void renderFiles(raw_ostream &OS, const coverage::CoverageMapping &Coverage, + ArrayRef SourceFiles, + ArrayRef FileReports, + bool ExportSummaryOnly) { + for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) + renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly); +} + +} // end anonymous namespace + +void CoverageExporterLcov::renderRoot(const CoverageFilters &IgnoreFilters) { + std::vector SourceFiles; + for (StringRef SF : Coverage.getUniqueSourceFiles()) { + if (!IgnoreFilters.matchesFilename(SF)) + SourceFiles.emplace_back(SF); + } + renderRoot(SourceFiles); +} + +void CoverageExporterLcov::renderRoot(ArrayRef SourceFiles) { + FileCoverageSummary Totals = FileCoverageSummary("Totals"); + auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals, + SourceFiles, Options); + renderFiles(OS, Coverage, SourceFiles, FileReports, + Options.ExportSummaryOnly); +} Index: llvm/trunk/tools/llvm-cov/CoverageViewOptions.h =================================================================== --- llvm/trunk/tools/llvm-cov/CoverageViewOptions.h +++ llvm/trunk/tools/llvm-cov/CoverageViewOptions.h @@ -20,7 +20,8 @@ struct CoverageViewOptions { enum class OutputFormat { Text, - HTML + HTML, + Lcov }; bool Debug; Index: llvm/trunk/tools/llvm-cov/SourceCoverageView.cpp =================================================================== --- llvm/trunk/tools/llvm-cov/SourceCoverageView.cpp +++ llvm/trunk/tools/llvm-cov/SourceCoverageView.cpp @@ -80,6 +80,10 @@ return llvm::make_unique(Opts); case CoverageViewOptions::OutputFormat::HTML: return llvm::make_unique(Opts); + case CoverageViewOptions::OutputFormat::Lcov: + // Unreachable because CodeCoverage.cpp should terminate with an error + // before we get here. + llvm_unreachable("Lcov format is not supported!"); } llvm_unreachable("Unknown coverage output format!"); } @@ -143,6 +147,10 @@ case CoverageViewOptions::OutputFormat::HTML: return llvm::make_unique( SourceName, File, Options, std::move(CoverageInfo)); + case CoverageViewOptions::OutputFormat::Lcov: + // Unreachable because CodeCoverage.cpp should terminate with an error + // before we get here. + llvm_unreachable("Lcov format is not supported!"); } llvm_unreachable("Unknown coverage output format!"); }