1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-18 18:42:46 +02:00

[llvm-cov] Add lcov tracefile export format.

Summary:
lcov tracefiles are used by various coverage reporting tools and build
systems (e.g., Bazel). It is a simple text-based format to parse and
more convenient to use than the JSON export format, which needs
additional processing to map regions/segments back to line numbers.

It's a little unfortunate that "text" format is now overloaded to refer
specifically to JSON for export, but I wanted to avoid making any
breaking changes to the UI of the llvm-cov tool at this time.

Patch by Tony Allevato (@allevato).

Reviewers: Dor1s, vsk

Reviewed By: Dor1s, vsk

Subscribers: mgorny, llvm-commits

Differential Revision: https://reviews.llvm.org/D54266

llvm-svn: 346506
This commit is contained in:
Max Moroz 2018-11-09 16:10:44 +00:00
parent ec1b2ec29d
commit 60087383d9
9 changed files with 270 additions and 13 deletions

View File

@ -374,9 +374,15 @@ SYNOPSIS
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 @@ OPTIONS
universal binary or to use an architecture that does not match a
non-universal binary.
.. option:: -format=<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=<PATTERN>

View File

@ -40,7 +40,8 @@ Non-comprehensive list of changes in this release
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

View File

@ -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

View File

@ -5,6 +5,7 @@ add_llvm_tool(llvm-cov
gcov.cpp
CodeCoverage.cpp
CoverageExporterJson.cpp
CoverageExporterLcov.cpp
CoverageFilters.cpp
CoverageReport.cpp
CoverageSummaryInfo.cpp

View File

@ -14,6 +14,7 @@
//===----------------------------------------------------------------------===//
#include "CoverageExporterJson.h"
#include "CoverageExporterLcov.h"
#include "CoverageFilters.h"
#include "CoverageReport.h"
#include "CoverageSummaryInfo.h"
@ -566,7 +567,9 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
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<std::string> PathRemap(
@ -674,6 +677,11 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
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 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
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 @@ int CodeCoverageTool::doReport(int argc, const char **argv,
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 @@ int CodeCoverageTool::doExport(int argc, const char **argv,
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 @@ int CodeCoverageTool::doExport(int argc, const char **argv,
return 1;
}
auto Exporter = CoverageExporterJson(*Coverage.get(), ViewOpts, outs());
std::unique_ptr<CoverageExporter> Exporter;
switch (ViewOpts.Format) {
case CoverageViewOptions::OutputFormat::Text:
Exporter = llvm::make_unique<CoverageExporterJson>(*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<CoverageExporterLcov>(*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;
}

View File

@ -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:<absolute path to source file>"
// - for each function:
// - "FN:<line number of function start>,<function name>"
// - for each function:
// - "FNDA:<execution count>,<function name>"
// - "FNF:<number of functions found>"
// - "FNH:<number of functions hit>"
// - for each instrumented line:
// - "DA:<line number>,<execution count>[,<checksum>]
// - "LH:<number of lines with non-zero execution count>"
// - "LF:<nubmer of instrumented lines>"
// - "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<coverage::FunctionRecordIterator> &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<std::string> SourceFiles,
ArrayRef<FileCoverageSummary> 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<std::string> SourceFiles;
for (StringRef SF : Coverage.getUniqueSourceFiles()) {
if (!IgnoreFilters.matchesFilename(SF))
SourceFiles.emplace_back(SF);
}
renderRoot(SourceFiles);
}
void CoverageExporterLcov::renderRoot(ArrayRef<std::string> SourceFiles) {
FileCoverageSummary Totals = FileCoverageSummary("Totals");
auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
SourceFiles, Options);
renderFiles(OS, Coverage, SourceFiles, FileReports,
Options.ExportSummaryOnly);
}

View File

@ -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<std::string> SourceFiles) override;
};
} // end namespace llvm
#endif // LLVM_COV_COVERAGEEXPORTERLCOV_H

View File

@ -20,7 +20,8 @@ namespace llvm {
struct CoverageViewOptions {
enum class OutputFormat {
Text,
HTML
HTML,
Lcov
};
bool Debug;

View File

@ -80,6 +80,10 @@ CoveragePrinter::create(const CoverageViewOptions &Opts) {
return llvm::make_unique<CoveragePrinterText>(Opts);
case CoverageViewOptions::OutputFormat::HTML:
return llvm::make_unique<CoveragePrinterHTML>(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 @@ SourceCoverageView::create(StringRef SourceName, const MemoryBuffer &File,
case CoverageViewOptions::OutputFormat::HTML:
return llvm::make_unique<SourceCoverageViewHTML>(
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!");
}