diff options
Diffstat (limited to 'contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp')
-rw-r--r-- | contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp new file mode 100644 index 0000000..ef50bba --- /dev/null +++ b/contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp @@ -0,0 +1,421 @@ +//===- CoverageExporterJson.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 JSON. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// +// The json code coverage export follows the following format +// Root: dict => Root Element containing metadata +// -- Data: array => Homogeneous array of one or more export objects +// ---- Export: dict => Json representation of one CoverageMapping +// ------ Files: array => List of objects describing coverage for files +// -------- File: dict => Coverage for a single file +// ---------- Segments: array => List of Segments contained in the file +// ------------ Segment: dict => Describes a segment of the file with a counter +// ---------- Expansions: array => List of expansion records +// ------------ Expansion: dict => Object that descibes a single expansion +// -------------- CountedRegion: dict => The region to be expanded +// -------------- TargetRegions: array => List of Regions in the expansion +// ---------------- CountedRegion: dict => Single Region in the expansion +// ---------- Summary: dict => Object summarizing the coverage for this file +// ------------ LineCoverage: dict => Object summarizing line coverage +// ------------ FunctionCoverage: dict => Object summarizing function coverage +// ------------ RegionCoverage: dict => Object summarizing region coverage +// ------ Functions: array => List of objects describing coverage for functions +// -------- Function: dict => Coverage info for a single function +// ---------- Filenames: array => List of filenames that the function relates to +// ---- Summary: dict => Object summarizing the coverage for the entire binary +// ------ LineCoverage: dict => Object summarizing line coverage +// ------ FunctionCoverage: dict => Object summarizing function coverage +// ------ InstantiationCoverage: dict => Object summarizing inst. coverage +// ------ RegionCoverage: dict => Object summarizing region coverage +// +//===----------------------------------------------------------------------===// + +#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" + +/// \brief Unique type identifier for JSON coverage export. +#define LLVM_COVERAGE_EXPORT_JSON_TYPE_STR "llvm.coverage.json.export" + +using namespace llvm; +using namespace coverage; + +class CoverageExporterJson { + /// \brief Output stream to print JSON to. + raw_ostream &OS; + + /// \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 << "\""; + } + + /// \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 << ":"; + 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); + } + + /// \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() { + State.pop(); + assert((State.size() >= 1) && "Closed too many JSON elements"); + OS << "}"; + } + + /// \brief Emit a starting array character. + void emitArrayStart() { + emitComma(); + State.push(JsonState::EmptyElement); + OS << "["; + } + + /// \brief Emit an array element. + template <typename V> void emitArrayElement(const V &Value) { + emitComma(); + emitSerialized(Value); + } + + /// \brief emit a closing array character. + void emitArrayEnd() { + State.pop(); + assert((State.size() >= 1) && "Closed too many JSON elements"); + OS << "]"; + } + + /// \brief Render the CoverageMapping object. + void renderRoot() { + // Start Root of JSON object. + emitDictStart(); + + emitDictElement("version", LLVM_COVERAGE_EXPORT_JSON_STR); + emitDictElement("type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR); + emitDictKey("data"); + + // Start List of Exports. + emitArrayStart(); + + // Start Export. + emitDictStart(); + + emitDictKey("files"); + + 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); + renderFiles(SourceFiles, FileReports); + + emitDictKey("functions"); + renderFunctions(Coverage.getCoveredFunctions()); + + 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()); + 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.NumLines); + emitDictElement("covered", Summary.LineCoverage.Covered); + emitDictElement("percent", Summary.LineCoverage.getPercentCovered()); + // End Line Coverage Summary. + emitDictEnd(); + + emitDictKey("functions"); + + // Start Function Coverage Summary. + emitDictStart(); + emitDictElement("count", Summary.FunctionCoverage.NumFunctions); + emitDictElement("covered", Summary.FunctionCoverage.Executed); + emitDictElement("percent", Summary.FunctionCoverage.getPercentCovered()); + // End Function Coverage Summary. + emitDictEnd(); + + emitDictKey("instantiations"); + + // Start Instantiation Coverage Summary. + emitDictStart(); + emitDictElement("count", Summary.InstantiationCoverage.NumFunctions); + emitDictElement("covered", Summary.InstantiationCoverage.Executed); + emitDictElement("percent", + Summary.InstantiationCoverage.getPercentCovered()); + // End Function Coverage Summary. + emitDictEnd(); + + emitDictKey("regions"); + + // Start Region Coverage Summary. + emitDictStart(); + emitDictElement("count", Summary.RegionCoverage.NumRegions); + emitDictElement("covered", Summary.RegionCoverage.Covered); + emitDictElement("notcovered", Summary.RegionCoverage.NotCovered); + emitDictElement("percent", Summary.RegionCoverage.getPercentCovered()); + // End Region Coverage Summary. + emitDictEnd(); + + // End Summary for the file. + emitDictEnd(); + } + +public: + CoverageExporterJson(const CoverageMapping &CoverageMapping, raw_ostream &OS) + : 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, + raw_ostream &OS) { + auto Exporter = CoverageExporterJson(CoverageMapping, OS); + + Exporter.print(); +} |