forked from OSchip/llvm-project
[llvm-cov] Refactor "export" command implementation and add support for SOURCES.
Summary: Define an interface for Exporter + split JSON exporter into .h and .cpp. Reviewers: vsk, morehouse Reviewed By: vsk Subscribers: llvm-commits, Dor1s, kcc Differential Revision: https://reviews.llvm.org/D41600 llvm-svn: 321815
This commit is contained in:
parent
a47289a2ee
commit
1ef3a778ac
|
@ -361,14 +361,15 @@ EXPORT COMMAND
|
|||
SYNOPSIS
|
||||
^^^^^^^^
|
||||
|
||||
:program:`llvm-cov export` [*options*] -instr-profile *PROFILE* *BIN* [*-object BIN,...*] [[*-object BIN*]]
|
||||
:program:`llvm-cov export` [*options*] -instr-profile *PROFILE* *BIN* [*-object BIN,...*] [[*-object BIN*]] [*SOURCES*]
|
||||
|
||||
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.
|
||||
*PROFILE* as JSON. It 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,
|
||||
see :ref:`llvm-cov-show`.
|
||||
|
|
|
@ -20,6 +20,20 @@ SHOW: {{.*}}sources_specified{{.*}}
|
|||
SHOW: {{.*}}sources_specified{{.*}}
|
||||
SHOW: {{.*}}sources_specified{{.*}}
|
||||
|
||||
|
||||
# Test "export" command. Use a temp .json file as output is a single line.
|
||||
RUN: llvm-cov export -instr-profile %S/Inputs/sources_specified/main.profdata \
|
||||
RUN: -path-equivalence=/tmp,%S/Inputs \
|
||||
RUN: %S/Inputs/sources_specified/main.covmapping \
|
||||
RUN: %S/Inputs/sources_specified/main.cc %S/Inputs/sources_specified/extra \
|
||||
RUN: > %t.export.json
|
||||
|
||||
RUN: not grep '"filename":"/tmp/sources_specified/abs.h"' %t.export.json
|
||||
RUN: grep '"filename":"/tmp/sources_specified/main.cc"' %t.export.json
|
||||
RUN: grep '"filename":"/tmp/sources_specified/extra/dec.h"' %t.export.json
|
||||
RUN: grep '"filename":"/tmp/sources_specified/extra/inc.h"' %t.export.json
|
||||
|
||||
|
||||
Instructions for regenerating the test:
|
||||
|
||||
# cd %S/Inputs/sources_specified
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CoverageExporterJson.h"
|
||||
#include "CoverageFilters.h"
|
||||
#include "CoverageReport.h"
|
||||
#include "CoverageSummaryInfo.h"
|
||||
|
@ -113,14 +114,14 @@ private:
|
|||
|
||||
typedef llvm::function_ref<int(int, const char **)> CommandLineParserType;
|
||||
|
||||
int show(int argc, const char **argv,
|
||||
CommandLineParserType commandLineParser);
|
||||
|
||||
int report(int argc, const char **argv,
|
||||
int doShow(int argc, const char **argv,
|
||||
CommandLineParserType commandLineParser);
|
||||
|
||||
int export_(int argc, const char **argv,
|
||||
CommandLineParserType commandLineParser);
|
||||
int doReport(int argc, const char **argv,
|
||||
CommandLineParserType commandLineParser);
|
||||
|
||||
int doExport(int argc, const char **argv,
|
||||
CommandLineParserType commandLineParser);
|
||||
|
||||
std::vector<StringRef> ObjectFilenames;
|
||||
CoverageViewOptions ViewOpts;
|
||||
|
@ -755,17 +756,17 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
|
|||
|
||||
switch (Cmd) {
|
||||
case Show:
|
||||
return show(argc, argv, commandLineParser);
|
||||
return doShow(argc, argv, commandLineParser);
|
||||
case Report:
|
||||
return report(argc, argv, commandLineParser);
|
||||
return doReport(argc, argv, commandLineParser);
|
||||
case Export:
|
||||
return export_(argc, argv, commandLineParser);
|
||||
return doExport(argc, argv, commandLineParser);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CodeCoverageTool::show(int argc, const char **argv,
|
||||
CommandLineParserType commandLineParser) {
|
||||
int CodeCoverageTool::doShow(int argc, const char **argv,
|
||||
CommandLineParserType commandLineParser) {
|
||||
|
||||
cl::OptionCategory ViewCategory("Viewing options");
|
||||
|
||||
|
@ -932,8 +933,8 @@ int CodeCoverageTool::show(int argc, const char **argv,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int CodeCoverageTool::report(int argc, const char **argv,
|
||||
CommandLineParserType commandLineParser) {
|
||||
int CodeCoverageTool::doReport(int argc, const char **argv,
|
||||
CommandLineParserType commandLineParser) {
|
||||
cl::opt<bool> ShowFunctionSummaries(
|
||||
"show-functions", cl::Optional, cl::init(false),
|
||||
cl::desc("Show coverage summaries for each function"));
|
||||
|
@ -969,8 +970,8 @@ int CodeCoverageTool::report(int argc, const char **argv,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int CodeCoverageTool::export_(int argc, const char **argv,
|
||||
CommandLineParserType commandLineParser) {
|
||||
int CodeCoverageTool::doExport(int argc, const char **argv,
|
||||
CommandLineParserType commandLineParser) {
|
||||
|
||||
auto Err = commandLineParser(argc, argv);
|
||||
if (Err)
|
||||
|
@ -987,7 +988,12 @@ int CodeCoverageTool::export_(int argc, const char **argv,
|
|||
return 1;
|
||||
}
|
||||
|
||||
exportCoverageDataToJson(*Coverage.get(), ViewOpts, outs());
|
||||
auto Exporter = CoverageExporterJson(*Coverage.get(), ViewOpts, outs());
|
||||
|
||||
if (SourceFiles.empty())
|
||||
Exporter.renderRoot();
|
||||
else
|
||||
Exporter.renderRoot(SourceFiles);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
//===- CoverageExporter.h - Code coverage exporter ------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This class defines a code coverage exporter interface.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_COV_COVERAGEEXPORTER_H
|
||||
#define LLVM_COV_COVERAGEEXPORTER_H
|
||||
|
||||
#include "CoverageSummaryInfo.h"
|
||||
#include "CoverageViewOptions.h"
|
||||
#include "llvm/ProfileData/Coverage/CoverageMapping.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// \brief Exports the code coverage information.
|
||||
class CoverageExporter {
|
||||
protected:
|
||||
/// \brief The full CoverageMapping object to export.
|
||||
const coverage::CoverageMapping &Coverage;
|
||||
|
||||
/// \brief The options passed to the tool.
|
||||
const CoverageViewOptions &Options;
|
||||
|
||||
/// \brief Output stream to print JSON to.
|
||||
raw_ostream &OS;
|
||||
|
||||
CoverageExporter(const coverage::CoverageMapping &CoverageMapping,
|
||||
const CoverageViewOptions &Options, raw_ostream &OS)
|
||||
: Coverage(CoverageMapping), Options(Options), OS(OS) {}
|
||||
|
||||
public:
|
||||
virtual ~CoverageExporter(){};
|
||||
|
||||
/// \brief Render the CoverageMapping object.
|
||||
virtual void renderRoot() = 0;
|
||||
|
||||
/// \brief Render the CoverageMapping object for specified source files.
|
||||
virtual void renderRoot(const std::vector<std::string> &SourceFiles) = 0;
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_COV_COVERAGEEXPORTER_H
|
|
@ -41,11 +41,8 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CoverageExporterJson.h"
|
||||
#include "CoverageReport.h"
|
||||
#include "CoverageSummaryInfo.h"
|
||||
#include "CoverageViewOptions.h"
|
||||
#include "llvm/ProfileData/Coverage/CoverageMapping.h"
|
||||
#include <stack>
|
||||
|
||||
/// \brief The semantic version combined as a string.
|
||||
#define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.0"
|
||||
|
@ -54,381 +51,328 @@
|
|||
#define LLVM_COVERAGE_EXPORT_JSON_TYPE_STR "llvm.coverage.json.export"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace coverage;
|
||||
|
||||
class CoverageExporterJson {
|
||||
const CoverageViewOptions &Options;
|
||||
CoverageExporterJson::CoverageExporterJson(
|
||||
const coverage::CoverageMapping &CoverageMapping,
|
||||
const CoverageViewOptions &Options, raw_ostream &OS)
|
||||
: CoverageExporter(CoverageMapping, Options, OS) {
|
||||
State.push(JsonState::None);
|
||||
}
|
||||
|
||||
/// \brief Output stream to print JSON to.
|
||||
raw_ostream &OS;
|
||||
void CoverageExporterJson::emitSerialized(const int64_t Value) { OS << Value; }
|
||||
|
||||
/// \brief The full CoverageMapping object to export.
|
||||
const CoverageMapping &Coverage;
|
||||
|
||||
/// \brief States that the JSON rendering machine can be in.
|
||||
enum JsonState { None, NonEmptyElement, EmptyElement };
|
||||
|
||||
/// \brief Tracks state of the JSON output.
|
||||
std::stack<JsonState> State;
|
||||
|
||||
/// \brief Emit a serialized scalar.
|
||||
void emitSerialized(const int64_t Value) { OS << Value; }
|
||||
|
||||
/// \brief Emit a serialized string.
|
||||
void emitSerialized(const std::string &Value) {
|
||||
OS << "\"";
|
||||
for (char C : Value) {
|
||||
if (C != '\\')
|
||||
OS << C;
|
||||
else
|
||||
OS << "\\\\";
|
||||
}
|
||||
OS << "\"";
|
||||
void CoverageExporterJson::emitSerialized(const std::string &Value) {
|
||||
OS << "\"";
|
||||
for (char C : Value) {
|
||||
if (C != '\\')
|
||||
OS << C;
|
||||
else
|
||||
OS << "\\\\";
|
||||
}
|
||||
OS << "\"";
|
||||
}
|
||||
|
||||
/// \brief Emit a comma if there is a previous element to delimit.
|
||||
void emitComma() {
|
||||
if (State.top() == JsonState::NonEmptyElement) {
|
||||
OS << ",";
|
||||
} else if (State.top() == JsonState::EmptyElement) {
|
||||
State.pop();
|
||||
assert((State.size() >= 1) && "Closed too many JSON elements");
|
||||
State.push(JsonState::NonEmptyElement);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Emit a starting dictionary/object character.
|
||||
void emitDictStart() {
|
||||
emitComma();
|
||||
State.push(JsonState::EmptyElement);
|
||||
OS << "{";
|
||||
}
|
||||
|
||||
/// \brief Emit a dictionary/object key but no value.
|
||||
void emitDictKey(const std::string &Key) {
|
||||
emitComma();
|
||||
emitSerialized(Key);
|
||||
OS << ":";
|
||||
void CoverageExporterJson::emitComma() {
|
||||
if (State.top() == JsonState::NonEmptyElement) {
|
||||
OS << ",";
|
||||
} else if (State.top() == JsonState::EmptyElement) {
|
||||
State.pop();
|
||||
assert((State.size() >= 1) && "Closed too many JSON elements");
|
||||
|
||||
// We do not want to emit a comma after this key.
|
||||
State.push(JsonState::EmptyElement);
|
||||
State.push(JsonState::NonEmptyElement);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Emit a dictionary/object key/value pair.
|
||||
template <typename V>
|
||||
void emitDictElement(const std::string &Key, const V &Value) {
|
||||
emitComma();
|
||||
emitSerialized(Key);
|
||||
OS << ":";
|
||||
emitSerialized(Value);
|
||||
}
|
||||
void CoverageExporterJson::emitDictStart() {
|
||||
emitComma();
|
||||
State.push(JsonState::EmptyElement);
|
||||
OS << "{";
|
||||
}
|
||||
|
||||
/// \brief Emit a closing dictionary/object character.
|
||||
void emitDictEnd() {
|
||||
State.pop();
|
||||
assert((State.size() >= 1) && "Closed too many JSON elements");
|
||||
OS << "}";
|
||||
}
|
||||
void CoverageExporterJson::emitDictKey(const std::string &Key) {
|
||||
emitComma();
|
||||
emitSerialized(Key);
|
||||
OS << ":";
|
||||
State.pop();
|
||||
assert((State.size() >= 1) && "Closed too many JSON elements");
|
||||
|
||||
/// \brief Emit a starting array character.
|
||||
void emitArrayStart() {
|
||||
emitComma();
|
||||
State.push(JsonState::EmptyElement);
|
||||
OS << "[";
|
||||
}
|
||||
// We do not want to emit a comma after this key.
|
||||
State.push(JsonState::EmptyElement);
|
||||
}
|
||||
|
||||
/// \brief Emit an array element.
|
||||
template <typename V> void emitArrayElement(const V &Value) {
|
||||
emitComma();
|
||||
emitSerialized(Value);
|
||||
}
|
||||
void CoverageExporterJson::emitDictEnd() {
|
||||
State.pop();
|
||||
assert((State.size() >= 1) && "Closed too many JSON elements");
|
||||
OS << "}";
|
||||
}
|
||||
|
||||
/// \brief emit a closing array character.
|
||||
void emitArrayEnd() {
|
||||
State.pop();
|
||||
assert((State.size() >= 1) && "Closed too many JSON elements");
|
||||
OS << "]";
|
||||
}
|
||||
void CoverageExporterJson::emitArrayStart() {
|
||||
emitComma();
|
||||
State.push(JsonState::EmptyElement);
|
||||
OS << "[";
|
||||
}
|
||||
|
||||
/// \brief Render the CoverageMapping object.
|
||||
void renderRoot() {
|
||||
// Start Root of JSON object.
|
||||
emitDictStart();
|
||||
void CoverageExporterJson::emitArrayEnd() {
|
||||
State.pop();
|
||||
assert((State.size() >= 1) && "Closed too many JSON elements");
|
||||
OS << "]";
|
||||
}
|
||||
|
||||
emitDictElement("version", LLVM_COVERAGE_EXPORT_JSON_STR);
|
||||
emitDictElement("type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR);
|
||||
emitDictKey("data");
|
||||
void CoverageExporterJson::renderRoot() {
|
||||
std::vector<std::string> SourceFiles;
|
||||
for (StringRef SF : Coverage.getUniqueSourceFiles())
|
||||
SourceFiles.emplace_back(SF);
|
||||
renderRoot(SourceFiles);
|
||||
}
|
||||
|
||||
// Start List of Exports.
|
||||
emitArrayStart();
|
||||
void CoverageExporterJson::renderRoot(
|
||||
const std::vector<std::string> &SourceFiles) {
|
||||
// Start Root of JSON object.
|
||||
emitDictStart();
|
||||
|
||||
// Start Export.
|
||||
emitDictStart();
|
||||
emitDictElement("version", LLVM_COVERAGE_EXPORT_JSON_STR);
|
||||
emitDictElement("type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR);
|
||||
emitDictKey("data");
|
||||
|
||||
emitDictKey("files");
|
||||
// Start List of Exports.
|
||||
emitArrayStart();
|
||||
|
||||
FileCoverageSummary Totals = FileCoverageSummary("Totals");
|
||||
std::vector<std::string> SourceFiles;
|
||||
for (StringRef SF : Coverage.getUniqueSourceFiles())
|
||||
SourceFiles.emplace_back(SF);
|
||||
auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
|
||||
SourceFiles, Options);
|
||||
renderFiles(SourceFiles, FileReports);
|
||||
// Start Export.
|
||||
emitDictStart();
|
||||
|
||||
// Skip functions-level information for summary-only export mode.
|
||||
if (!Options.ExportSummaryOnly) {
|
||||
emitDictKey("functions");
|
||||
renderFunctions(Coverage.getCoveredFunctions());
|
||||
}
|
||||
emitDictKey("files");
|
||||
|
||||
emitDictKey("totals");
|
||||
renderSummary(Totals);
|
||||
|
||||
// End Export.
|
||||
emitDictEnd();
|
||||
|
||||
// End List of Exports.
|
||||
emitArrayEnd();
|
||||
|
||||
// End Root of JSON Object.
|
||||
emitDictEnd();
|
||||
|
||||
assert((State.top() == JsonState::None) &&
|
||||
"All Elements In JSON were Closed");
|
||||
}
|
||||
|
||||
/// \brief Render an array of all the given functions.
|
||||
void
|
||||
renderFunctions(const iterator_range<FunctionRecordIterator> &Functions) {
|
||||
// Start List of Functions.
|
||||
emitArrayStart();
|
||||
|
||||
for (const auto &Function : Functions) {
|
||||
// Start Function.
|
||||
emitDictStart();
|
||||
|
||||
emitDictElement("name", Function.Name);
|
||||
emitDictElement("count", Function.ExecutionCount);
|
||||
emitDictKey("regions");
|
||||
|
||||
renderRegions(Function.CountedRegions);
|
||||
|
||||
emitDictKey("filenames");
|
||||
|
||||
// Start Filenames for Function.
|
||||
emitArrayStart();
|
||||
|
||||
for (const auto &FileName : Function.Filenames)
|
||||
emitArrayElement(FileName);
|
||||
|
||||
// End Filenames for Function.
|
||||
emitArrayEnd();
|
||||
|
||||
// End Function.
|
||||
emitDictEnd();
|
||||
}
|
||||
|
||||
// End List of Functions.
|
||||
emitArrayEnd();
|
||||
}
|
||||
|
||||
/// \brief Render an array of all the source files, also pass back a Summary.
|
||||
void renderFiles(ArrayRef<std::string> SourceFiles,
|
||||
ArrayRef<FileCoverageSummary> FileReports) {
|
||||
// Start List of Files.
|
||||
emitArrayStart();
|
||||
|
||||
for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) {
|
||||
// Render the file.
|
||||
auto FileCoverage = Coverage.getCoverageForFile(SourceFiles[I]);
|
||||
renderFile(FileCoverage, FileReports[I]);
|
||||
}
|
||||
|
||||
// End List of Files.
|
||||
emitArrayEnd();
|
||||
}
|
||||
|
||||
/// \brief Render a single file.
|
||||
void renderFile(const CoverageData &FileCoverage,
|
||||
const FileCoverageSummary &FileReport) {
|
||||
// Start File.
|
||||
emitDictStart();
|
||||
|
||||
emitDictElement("filename", FileCoverage.getFilename());
|
||||
|
||||
// Skip segments and expansions for summary-only export mode.
|
||||
if (!Options.ExportSummaryOnly) {
|
||||
emitDictKey("segments");
|
||||
|
||||
// Start List of Segments.
|
||||
emitArrayStart();
|
||||
|
||||
for (const auto &Segment : FileCoverage)
|
||||
renderSegment(Segment);
|
||||
|
||||
// End List of Segments.
|
||||
emitArrayEnd();
|
||||
|
||||
emitDictKey("expansions");
|
||||
|
||||
// Start List of Expansions.
|
||||
emitArrayStart();
|
||||
|
||||
for (const auto &Expansion : FileCoverage.getExpansions())
|
||||
renderExpansion(Expansion);
|
||||
|
||||
// End List of Expansions.
|
||||
emitArrayEnd();
|
||||
}
|
||||
|
||||
emitDictKey("summary");
|
||||
renderSummary(FileReport);
|
||||
|
||||
// End File.
|
||||
emitDictEnd();
|
||||
}
|
||||
|
||||
/// \brief Render a CoverageSegment.
|
||||
void renderSegment(const CoverageSegment &Segment) {
|
||||
// Start Segment.
|
||||
emitArrayStart();
|
||||
|
||||
emitArrayElement(Segment.Line);
|
||||
emitArrayElement(Segment.Col);
|
||||
emitArrayElement(Segment.Count);
|
||||
emitArrayElement(Segment.HasCount);
|
||||
emitArrayElement(Segment.IsRegionEntry);
|
||||
|
||||
// End Segment.
|
||||
emitArrayEnd();
|
||||
}
|
||||
|
||||
/// \brief Render an ExpansionRecord.
|
||||
void renderExpansion(const ExpansionRecord &Expansion) {
|
||||
// Start Expansion.
|
||||
emitDictStart();
|
||||
|
||||
// Mark the beginning and end of this expansion in the source file.
|
||||
emitDictKey("source_region");
|
||||
renderRegion(Expansion.Region);
|
||||
|
||||
// Enumerate the coverage information for the expansion.
|
||||
emitDictKey("target_regions");
|
||||
renderRegions(Expansion.Function.CountedRegions);
|
||||
|
||||
emitDictKey("filenames");
|
||||
// Start List of Filenames to map the fileIDs.
|
||||
emitArrayStart();
|
||||
for (const auto &Filename : Expansion.Function.Filenames)
|
||||
emitArrayElement(Filename);
|
||||
// End List of Filenames.
|
||||
emitArrayEnd();
|
||||
|
||||
// End Expansion.
|
||||
emitDictEnd();
|
||||
}
|
||||
|
||||
/// \brief Render a list of CountedRegions.
|
||||
void renderRegions(ArrayRef<CountedRegion> Regions) {
|
||||
// Start List of Regions.
|
||||
emitArrayStart();
|
||||
|
||||
for (const auto &Region : Regions)
|
||||
renderRegion(Region);
|
||||
|
||||
// End List of Regions.
|
||||
emitArrayEnd();
|
||||
}
|
||||
|
||||
/// \brief Render a single CountedRegion.
|
||||
void renderRegion(const CountedRegion &Region) {
|
||||
// Start CountedRegion.
|
||||
emitArrayStart();
|
||||
|
||||
emitArrayElement(Region.LineStart);
|
||||
emitArrayElement(Region.ColumnStart);
|
||||
emitArrayElement(Region.LineEnd);
|
||||
emitArrayElement(Region.ColumnEnd);
|
||||
emitArrayElement(Region.ExecutionCount);
|
||||
emitArrayElement(Region.FileID);
|
||||
emitArrayElement(Region.ExpandedFileID);
|
||||
emitArrayElement(Region.Kind);
|
||||
|
||||
// End CountedRegion.
|
||||
emitArrayEnd();
|
||||
}
|
||||
|
||||
/// \brief Render a FileCoverageSummary.
|
||||
void renderSummary(const FileCoverageSummary &Summary) {
|
||||
// Start Summary for the file.
|
||||
emitDictStart();
|
||||
|
||||
emitDictKey("lines");
|
||||
|
||||
// Start Line Coverage Summary.
|
||||
emitDictStart();
|
||||
emitDictElement("count", Summary.LineCoverage.getNumLines());
|
||||
emitDictElement("covered", Summary.LineCoverage.getCovered());
|
||||
emitDictElement("percent", Summary.LineCoverage.getPercentCovered());
|
||||
// End Line Coverage Summary.
|
||||
emitDictEnd();
|
||||
FileCoverageSummary Totals = FileCoverageSummary("Totals");
|
||||
auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
|
||||
SourceFiles, Options);
|
||||
renderFiles(SourceFiles, FileReports);
|
||||
|
||||
// Skip functions-level information for summary-only export mode.
|
||||
if (!Options.ExportSummaryOnly) {
|
||||
emitDictKey("functions");
|
||||
renderFunctions(Coverage.getCoveredFunctions());
|
||||
}
|
||||
|
||||
// Start Function Coverage Summary.
|
||||
emitDictKey("totals");
|
||||
renderSummary(Totals);
|
||||
|
||||
// End Export.
|
||||
emitDictEnd();
|
||||
|
||||
// End List of Exports.
|
||||
emitArrayEnd();
|
||||
|
||||
// End Root of JSON Object.
|
||||
emitDictEnd();
|
||||
|
||||
assert((State.top() == JsonState::None) &&
|
||||
"All Elements In JSON were Closed");
|
||||
}
|
||||
|
||||
void CoverageExporterJson::renderFunctions(
|
||||
const iterator_range<coverage::FunctionRecordIterator> &Functions) {
|
||||
// Start List of Functions.
|
||||
emitArrayStart();
|
||||
|
||||
for (const auto &Function : Functions) {
|
||||
// Start Function.
|
||||
emitDictStart();
|
||||
emitDictElement("count", Summary.FunctionCoverage.getNumFunctions());
|
||||
emitDictElement("covered", Summary.FunctionCoverage.getExecuted());
|
||||
emitDictElement("percent", Summary.FunctionCoverage.getPercentCovered());
|
||||
// End Function Coverage Summary.
|
||||
emitDictEnd();
|
||||
|
||||
emitDictKey("instantiations");
|
||||
|
||||
// Start Instantiation Coverage Summary.
|
||||
emitDictStart();
|
||||
emitDictElement("count", Summary.InstantiationCoverage.getNumFunctions());
|
||||
emitDictElement("covered", Summary.InstantiationCoverage.getExecuted());
|
||||
emitDictElement("percent",
|
||||
Summary.InstantiationCoverage.getPercentCovered());
|
||||
// End Function Coverage Summary.
|
||||
emitDictEnd();
|
||||
|
||||
emitDictElement("name", Function.Name);
|
||||
emitDictElement("count", Function.ExecutionCount);
|
||||
emitDictKey("regions");
|
||||
|
||||
// Start Region Coverage Summary.
|
||||
emitDictStart();
|
||||
emitDictElement("count", Summary.RegionCoverage.getNumRegions());
|
||||
emitDictElement("covered", Summary.RegionCoverage.getCovered());
|
||||
emitDictElement("notcovered",
|
||||
Summary.RegionCoverage.getNumRegions() -
|
||||
Summary.RegionCoverage.getCovered());
|
||||
emitDictElement("percent", Summary.RegionCoverage.getPercentCovered());
|
||||
// End Region Coverage Summary.
|
||||
emitDictEnd();
|
||||
renderRegions(Function.CountedRegions);
|
||||
|
||||
// End Summary for the file.
|
||||
emitDictKey("filenames");
|
||||
|
||||
// Start Filenames for Function.
|
||||
emitArrayStart();
|
||||
|
||||
for (const auto &FileName : Function.Filenames)
|
||||
emitArrayElement(FileName);
|
||||
|
||||
// End Filenames for Function.
|
||||
emitArrayEnd();
|
||||
|
||||
// End Function.
|
||||
emitDictEnd();
|
||||
}
|
||||
|
||||
public:
|
||||
CoverageExporterJson(const CoverageMapping &CoverageMapping,
|
||||
const CoverageViewOptions &Options, raw_ostream &OS)
|
||||
: Options(Options), OS(OS), Coverage(CoverageMapping) {
|
||||
State.push(JsonState::None);
|
||||
}
|
||||
|
||||
/// \brief Print the CoverageMapping.
|
||||
void print() { renderRoot(); }
|
||||
};
|
||||
|
||||
/// \brief Export the given CoverageMapping to a JSON Format.
|
||||
void exportCoverageDataToJson(const CoverageMapping &CoverageMapping,
|
||||
const CoverageViewOptions &Options,
|
||||
raw_ostream &OS) {
|
||||
auto Exporter = CoverageExporterJson(CoverageMapping, Options, OS);
|
||||
|
||||
Exporter.print();
|
||||
// End List of Functions.
|
||||
emitArrayEnd();
|
||||
}
|
||||
|
||||
void CoverageExporterJson::renderFiles(
|
||||
ArrayRef<std::string> SourceFiles,
|
||||
ArrayRef<FileCoverageSummary> FileReports) {
|
||||
// Start List of Files.
|
||||
emitArrayStart();
|
||||
|
||||
for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) {
|
||||
// Render the file.
|
||||
auto FileCoverage = Coverage.getCoverageForFile(SourceFiles[I]);
|
||||
renderFile(FileCoverage, FileReports[I]);
|
||||
}
|
||||
|
||||
// End List of Files.
|
||||
emitArrayEnd();
|
||||
}
|
||||
|
||||
void CoverageExporterJson::renderFile(
|
||||
const coverage::CoverageData &FileCoverage,
|
||||
const FileCoverageSummary &FileReport) {
|
||||
// Start File.
|
||||
emitDictStart();
|
||||
|
||||
emitDictElement("filename", FileCoverage.getFilename());
|
||||
|
||||
// Skip segments and expansions for summary-only export mode.
|
||||
if (!Options.ExportSummaryOnly) {
|
||||
emitDictKey("segments");
|
||||
|
||||
// Start List of Segments.
|
||||
emitArrayStart();
|
||||
|
||||
for (const auto &Segment : FileCoverage)
|
||||
renderSegment(Segment);
|
||||
|
||||
// End List of Segments.
|
||||
emitArrayEnd();
|
||||
|
||||
emitDictKey("expansions");
|
||||
|
||||
// Start List of Expansions.
|
||||
emitArrayStart();
|
||||
|
||||
for (const auto &Expansion : FileCoverage.getExpansions())
|
||||
renderExpansion(Expansion);
|
||||
|
||||
// End List of Expansions.
|
||||
emitArrayEnd();
|
||||
}
|
||||
|
||||
emitDictKey("summary");
|
||||
renderSummary(FileReport);
|
||||
|
||||
// End File.
|
||||
emitDictEnd();
|
||||
}
|
||||
|
||||
void CoverageExporterJson::renderSegment(
|
||||
const coverage::CoverageSegment &Segment) {
|
||||
// Start Segment.
|
||||
emitArrayStart();
|
||||
|
||||
emitArrayElement(Segment.Line);
|
||||
emitArrayElement(Segment.Col);
|
||||
emitArrayElement(Segment.Count);
|
||||
emitArrayElement(Segment.HasCount);
|
||||
emitArrayElement(Segment.IsRegionEntry);
|
||||
|
||||
// End Segment.
|
||||
emitArrayEnd();
|
||||
}
|
||||
|
||||
void CoverageExporterJson::renderExpansion(
|
||||
const coverage::ExpansionRecord &Expansion) {
|
||||
// Start Expansion.
|
||||
emitDictStart();
|
||||
|
||||
// Mark the beginning and end of this expansion in the source file.
|
||||
emitDictKey("source_region");
|
||||
renderRegion(Expansion.Region);
|
||||
|
||||
// Enumerate the coverage information for the expansion.
|
||||
emitDictKey("target_regions");
|
||||
renderRegions(Expansion.Function.CountedRegions);
|
||||
|
||||
emitDictKey("filenames");
|
||||
// Start List of Filenames to map the fileIDs.
|
||||
emitArrayStart();
|
||||
for (const auto &Filename : Expansion.Function.Filenames)
|
||||
emitArrayElement(Filename);
|
||||
// End List of Filenames.
|
||||
emitArrayEnd();
|
||||
|
||||
// End Expansion.
|
||||
emitDictEnd();
|
||||
}
|
||||
|
||||
void CoverageExporterJson::renderRegions(
|
||||
ArrayRef<coverage::CountedRegion> Regions) {
|
||||
// Start List of Regions.
|
||||
emitArrayStart();
|
||||
|
||||
for (const auto &Region : Regions)
|
||||
renderRegion(Region);
|
||||
|
||||
// End List of Regions.
|
||||
emitArrayEnd();
|
||||
}
|
||||
|
||||
void CoverageExporterJson::renderRegion(const coverage::CountedRegion &Region) {
|
||||
// Start CountedRegion.
|
||||
emitArrayStart();
|
||||
|
||||
emitArrayElement(Region.LineStart);
|
||||
emitArrayElement(Region.ColumnStart);
|
||||
emitArrayElement(Region.LineEnd);
|
||||
emitArrayElement(Region.ColumnEnd);
|
||||
emitArrayElement(Region.ExecutionCount);
|
||||
emitArrayElement(Region.FileID);
|
||||
emitArrayElement(Region.ExpandedFileID);
|
||||
emitArrayElement(Region.Kind);
|
||||
|
||||
// End CountedRegion.
|
||||
emitArrayEnd();
|
||||
}
|
||||
|
||||
void CoverageExporterJson::renderSummary(const FileCoverageSummary &Summary) {
|
||||
// Start Summary for the file.
|
||||
emitDictStart();
|
||||
|
||||
emitDictKey("lines");
|
||||
|
||||
// Start Line Coverage Summary.
|
||||
emitDictStart();
|
||||
emitDictElement("count", Summary.LineCoverage.getNumLines());
|
||||
emitDictElement("covered", Summary.LineCoverage.getCovered());
|
||||
emitDictElement("percent", Summary.LineCoverage.getPercentCovered());
|
||||
// End Line Coverage Summary.
|
||||
emitDictEnd();
|
||||
|
||||
emitDictKey("functions");
|
||||
|
||||
// Start Function Coverage Summary.
|
||||
emitDictStart();
|
||||
emitDictElement("count", Summary.FunctionCoverage.getNumFunctions());
|
||||
emitDictElement("covered", Summary.FunctionCoverage.getExecuted());
|
||||
emitDictElement("percent", Summary.FunctionCoverage.getPercentCovered());
|
||||
// End Function Coverage Summary.
|
||||
emitDictEnd();
|
||||
|
||||
emitDictKey("instantiations");
|
||||
|
||||
// Start Instantiation Coverage Summary.
|
||||
emitDictStart();
|
||||
emitDictElement("count", Summary.InstantiationCoverage.getNumFunctions());
|
||||
emitDictElement("covered", Summary.InstantiationCoverage.getExecuted());
|
||||
emitDictElement("percent", Summary.InstantiationCoverage.getPercentCovered());
|
||||
// End Function Coverage Summary.
|
||||
emitDictEnd();
|
||||
|
||||
emitDictKey("regions");
|
||||
|
||||
// Start Region Coverage Summary.
|
||||
emitDictStart();
|
||||
emitDictElement("count", Summary.RegionCoverage.getNumRegions());
|
||||
emitDictElement("covered", Summary.RegionCoverage.getCovered());
|
||||
emitDictElement("notcovered", Summary.RegionCoverage.getNumRegions() -
|
||||
Summary.RegionCoverage.getCovered());
|
||||
emitDictElement("percent", Summary.RegionCoverage.getPercentCovered());
|
||||
// End Region Coverage Summary.
|
||||
emitDictEnd();
|
||||
|
||||
// End Summary for the file.
|
||||
emitDictEnd();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
//===- CoverageExporterJson.h - Code coverage JSON 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 JSON format.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_COV_COVERAGEEXPORTERJSON_H
|
||||
#define LLVM_COV_COVERAGEEXPORTERJSON_H
|
||||
|
||||
#include "CoverageExporter.h"
|
||||
#include <stack>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class CoverageExporterJson : public CoverageExporter {
|
||||
/// \brief States that the JSON rendering machine can be in.
|
||||
enum JsonState { None, NonEmptyElement, EmptyElement };
|
||||
|
||||
/// \brief Tracks state of the JSON output.
|
||||
std::stack<JsonState> State;
|
||||
|
||||
/// \brief Emit a serialized scalar.
|
||||
void emitSerialized(const int64_t Value);
|
||||
|
||||
/// \brief Emit a serialized string.
|
||||
void emitSerialized(const std::string &Value);
|
||||
|
||||
/// \brief Emit a comma if there is a previous element to delimit.
|
||||
void emitComma();
|
||||
|
||||
/// \brief Emit a starting dictionary/object character.
|
||||
void emitDictStart();
|
||||
|
||||
/// \brief Emit a dictionary/object key but no value.
|
||||
void emitDictKey(const std::string &Key);
|
||||
|
||||
/// \brief Emit a dictionary/object key/value pair.
|
||||
template <typename V>
|
||||
void emitDictElement(const std::string &Key, const V &Value) {
|
||||
emitComma();
|
||||
emitSerialized(Key);
|
||||
OS << ":";
|
||||
emitSerialized(Value);
|
||||
}
|
||||
|
||||
/// \brief Emit a closing dictionary/object character.
|
||||
void emitDictEnd();
|
||||
|
||||
/// \brief Emit a starting array character.
|
||||
void emitArrayStart();
|
||||
|
||||
/// \brief Emit an array element.
|
||||
template <typename V> void emitArrayElement(const V &Value) {
|
||||
emitComma();
|
||||
emitSerialized(Value);
|
||||
}
|
||||
|
||||
/// \brief emit a closing array character.
|
||||
void emitArrayEnd();
|
||||
|
||||
/// \brief Render an array of all the given functions.
|
||||
void renderFunctions(
|
||||
const iterator_range<coverage::FunctionRecordIterator> &Functions);
|
||||
|
||||
/// \brief Render an array of all the source files, also pass back a Summary.
|
||||
void renderFiles(ArrayRef<std::string> SourceFiles,
|
||||
ArrayRef<FileCoverageSummary> FileReports);
|
||||
|
||||
/// \brief Render a single file.
|
||||
void renderFile(const coverage::CoverageData &FileCoverage,
|
||||
const FileCoverageSummary &FileReport);
|
||||
|
||||
/// \brief Render a CoverageSegment.
|
||||
void renderSegment(const coverage::CoverageSegment &Segment);
|
||||
|
||||
/// \brief Render an ExpansionRecord.
|
||||
void renderExpansion(const coverage::ExpansionRecord &Expansion);
|
||||
|
||||
/// \brief Render a list of CountedRegions.
|
||||
void renderRegions(ArrayRef<coverage::CountedRegion> Regions);
|
||||
|
||||
/// \brief Render a single CountedRegion.
|
||||
void renderRegion(const coverage::CountedRegion &Region);
|
||||
|
||||
/// \brief Render a FileCoverageSummary.
|
||||
void renderSummary(const FileCoverageSummary &Summary);
|
||||
|
||||
public:
|
||||
CoverageExporterJson(const coverage::CoverageMapping &CoverageMapping,
|
||||
const CoverageViewOptions &Options, raw_ostream &OS);
|
||||
|
||||
/// \brief Render the CoverageMapping object.
|
||||
void renderRoot() override;
|
||||
|
||||
/// \brief Render the CoverageMapping object for specified source files.
|
||||
void renderRoot(const std::vector<std::string> &SourceFiles) override;
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_COV_COVERAGEEXPORTERJSON_H
|
|
@ -1,4 +1,4 @@
|
|||
//===- CoverageReport.h - Code coverage report ---------------------------===//
|
||||
//===- CoverageReport.h - Code coverage report ----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
|
|
Loading…
Reference in New Issue