* :ref:`gcov <llvm-cov-gcov>`
* :ref:`show <llvm-cov-show>`
* :ref:`report <llvm-cov-report>`
+* :ref:`export <llvm-cov-export>`
.. program:: llvm-cov gcov
It is an error to specify an architecture that is not included in the
universal binary or to use an architecture that does not match a
non-universal binary.
+
+EXPORT COMMAND
+--------------
+
+SYNOPSIS
+^^^^^^^^
+
+:program:`llvm-cov export` [*options*] -instr-profile *PROFILE* *BIN*
+
+DESCRIPTION
+^^^^^^^^^^^
+
+The :program:`llvm-cov export` command exports regions, functions, expansions,
+and summaries of the coverage of a binary *BIN* using the profile data
+*PROFILE* as JSON.
+
+For information on compiling programs for coverage and generating profile data,
+see :ref:`llvm-cov-show`.
+
+OPTIONS
+^^^^^^^
+
+.. option:: -arch=<name>
+
+ If the covered binary is a universal binary, select the architecture to use.
+ It is an error to specify an architecture that is not included in the
+ universal binary or to use an architecture that does not match a
+ non-universal binary.
--- /dev/null
+// Metadata section
+// CHECK: {"version":"1.0.0","type":"llvm.coverage.json.export","data":[
+
+// Open Export
+// CHECK-SAME: {"object":"{{[^"]+}}","files":[
+
+// File Object
+// CHECK-SAME: {"filename":"{{[^"]+}}binary-formats.c",
+// CHECK-SAME: "segments":[
+// CHECK-SAME: [4,40,100,1,1],[4,42,0,0,0]],
+// CHECK-SAME: "expansions":[],
+
+// Verify the Summary Section for the first file
+// CHECK-SAME: "summary":{
+// CHECK-SAME: "lines":{"count":1,"covered":1,"percent":100,"noncode":0},
+// CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
+// CHECK-SAME: "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}}}
+
+// Close Files Array
+// CHECK-SAME: ],
+
+// Functions List
+// CHECK-SAME: "functions":[
+// CHECK-SAME: {"name":"main","count":100,"regions":[
+// CHECK-SAME: [4,40,4,42,100,0,0,0]
+// CHECK-SAME: ],
+// CHECK-SAME: "filenames":["{{[^"]+}}binary-formats.c"]
+// CHECK-SAME: }],
+
+
+// Full Export Summary
+// CHECK-SAME: "totals":{
+// CHECK-SAME: "lines":{"count":1,"covered":1,"percent":100,"noncode":0},
+// CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
+// CHECK-SAME: "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}}
+
+// Close the export object, data array, and root object
+// CHECK-SAME: }]}
--- /dev/null
+// Metadata section
+// CHECK: {"version":"1.0.0","type":"llvm.coverage.json.export","data":[
+
+// Open Export
+// CHECK-SAME: {"object":"{{[^"]+}}","files":[
+
+// File Object
+// CHECK-SAME: {"filename":"{{[^"]+}}showHighlightedRanges.cpp",
+// CHECK-SAME: "segments":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}],
+// CHECK-SAME: "expansions":[],
+
+// Verify the Summary Section for the first file
+// CHECK-SAME: "summary":{
+// CHECK-SAME: "lines":{"count":40,"covered":26,"percent":65,"noncode":0},
+// CHECK-SAME: "functions":{"count":4,"covered":4,"percent":100},
+// CHECK-SAME: "regions":{"count":19,"covered":11,"notcovered":8,"percent":57}}}
+
+// Close Files Array
+// CHECK-SAME: ],
+
+// Functions List
+// CHECK-SAME: "functions":[
+// CHECK-SAME: {"name":"_Z4funcv","count":1,"regions":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}
+// CHECK-SAME: ],
+// CHECK-SAME: "filenames":["{{[^"]+}}showHighlightedRanges.cpp"]
+// CHECK-SAME: },
+// CHECK-SAME: {"name":"_Z5func2i","count":1,"regions":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}
+// CHECK-SAME: ],
+// CHECK-SAME: "filenames":["{{[^"]+}}showHighlightedRanges.cpp"]
+// CHECK-SAME: }
+// CHECK-SAME: {"name":"_Z4testv","count":1,"regions":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}
+// CHECK-SAME: ],
+// CHECK-SAME: "filenames":["{{[^"]+}}showHighlightedRanges.cpp"]
+// CHECK-SAME: }
+// CHECK-SAME: {"name":"main","count":1,"regions":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}
+// CHECK-SAME: ],
+// CHECK-SAME: "filenames":["{{.*}}showHighlightedRanges.cpp"]
+// CHECK-SAME: }],
+
+
+// Full Export Summary
+// CHECK-SAME: "totals":{
+// CHECK-SAME: "lines":{"count":40,"covered":26,"percent":65,"noncode":0},
+// CHECK-SAME: "functions":{"count":4,"covered":4,"percent":100},
+// CHECK-SAME: "regions":{"count":19,"covered":11,"notcovered":8,"percent":57}}
+
+// Close the export object, data array, and root object
+// CHECK-SAME: }]}
--- /dev/null
+// Metadata section
+// CHECK: {"version":"1.0.0","type":"llvm.coverage.json.export","data":[
+
+// Open Export
+// CHECK-SAME: {"object":"{{[^"]+}}","files":[
+
+// File Object
+// CHECK-SAME: {"filename":"{{[^"]+}}showLineExecutionCounts.cpp",
+// CHECK-SAME: "segments":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}],
+// CHECK-SAME: "expansions":[],
+
+// Verify the Summary Section for the first file
+// CHECK-SAME: "summary":{
+// CHECK-SAME: "lines":{"count":20,"covered":16,"percent":80,"noncode":0},
+// CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
+// CHECK-SAME: "regions":{"count":10,"covered":7,"notcovered":3,"percent":70}}}
+
+// Close Files Array
+// CHECK-SAME: ],
+
+// Functions List
+// CHECK-SAME: "functions":[
+// CHECK-SAME: {"name":"main","count":161,"regions":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}
+// CHECK-SAME: ],
+// CHECK-SAME: "filenames":["{{[^"]+}}showLineExecutionCounts.cpp"]
+// CHECK-SAME: }],
+
+
+// Full Export Summary
+// CHECK-SAME: "totals":{
+// CHECK-SAME: "lines":{"count":20,"covered":16,"percent":80,"noncode":0},
+// CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
+// CHECK-SAME: "regions":{"count":10,"covered":7,"notcovered":3,"percent":70}}
+
+// Close the export object, data array, and root object
+// CHECK-SAME: }]}
--- /dev/null
+// Metadata section
+// CHECK: {"version":"1.0.0","type":"llvm.coverage.json.export","data":[
+
+// Open Export
+// CHECK-SAME: {"object":"{{[^"]+}}","files":[
+
+// File Object
+// CHECK-SAME: {"filename":"{{[^"]+}}showRegionMarkers.cpp",
+// CHECK-SAME: "segments":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}],
+// CHECK-SAME: "expansions":[],
+
+// Verify the Summary Section for the first file
+// CHECK-SAME: "summary":{
+// CHECK-SAME: "lines":{"count":21,"covered":17,"percent":80,"noncode":0},
+// CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
+// CHECK-SAME: "regions":{"count":10,"covered":7,"notcovered":3,"percent":70}}
+
+// Close Files Array
+// CHECK-SAME: ],
+
+// Functions List
+// CHECK-SAME: "functions":[
+// CHECK-SAME: {"name":"main","count":1111000,"regions":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}
+// CHECK-SAME: ],
+// CHECK-SAME: "filenames":["{{[^"]+}}showRegionMarkers.cpp"]
+// CHECK-SAME: }],
+
+// Full Export Summary
+// CHECK-SAME: "totals":{
+// CHECK-SAME: "lines":{"count":21,"covered":17,"percent":80,"noncode":0},
+// CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
+// CHECK-SAME: "regions":{"count":10,"covered":7,"notcovered":3,"percent":70}}
+
+// Close the export object, data array, and root object
+// CHECK-SAME: }]}
--- /dev/null
+// Metadata section
+// CHECK: {"version":"1.0.0","type":"llvm.coverage.json.export","data":[
+
+// Open Export
+// CHECK-SAME: {"object":"{{[^"]+}}","files":[
+
+// File Object
+// CHECK-SAME: {"filename":"{{[^"]+}}showExpansions.cpp",
+// CHECK-SAME: "segments":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}],
+// CHECK-SAME: "expansions":[
+// CHECK-SAME: {"source_region":[24,5,24,17,100,0,1,1],
+// CHECK-SAME: "target_regions":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}
+// CHECK-SAME: ],
+
+// Yes, 4 of the same filename in a row
+// CHECK-SAME: "filenames":[
+// CHECK-SAME: "{{[^"]+}}showExpansions.cpp","{{[^"]+}}showExpansions.cpp",
+// CHECK-SAME: "{{[^"]+}}showExpansions.cpp","{{[^"]+}}showExpansions.cpp"]
+// CHECK-SAME: }],
+
+// Verify the Summary Section for the first file
+// CHECK-SAME: "summary":{
+// CHECK-SAME: "lines":{"count":17,"covered":15,"percent":88,"noncode":0},
+// CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
+// CHECK-SAME: "regions":{"count":13,"covered":12,"notcovered":1,"percent":92}}
+
+// Close Files Array
+// CHECK-SAME: ],
+
+// Functions List
+// CHECK-SAME: "functions":[
+// CHECK-SAME: {"name":"main","count":1,"regions":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}
+// CHECK-SAME: ],
+// CHECK-SAME: "filenames":[
+// CHECK-SAME: "{{[^"]+}}showExpansions.cpp",
+// CHECK-SAME: "{{[^"]+}}showExpansions.cpp",
+// CHECK-SAME: "{{[^"]+}}showExpansions.cpp",
+// CHECK-SAME: "{{[^"]+}}showExpansions.cpp"]
+// CHECK-SAME: }],
+
+// Full Export Summary
+// CHECK-SAME: "totals":{
+// CHECK-SAME: "lines":{"count":17,"covered":15,"percent":88,"noncode":0},
+// CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
+// CHECK-SAME: "regions":{"count":13,"covered":12,"notcovered":1,"percent":92}}
+
+// Close the export object, data array, and root object
+// CHECK-SAME: }]}
--- /dev/null
+// Metadata section
+// CHECK: {"version":"1.0.0","type":"llvm.coverage.json.export","data":[
+
+// Open Export
+// CHECK-SAME: {"object":"{{[^"]+}}","files":[
+
+// File Object
+// CHECK-SAME: {"filename":"{{[^"]+}}universal-binary.c",
+// CHECK-SAME: "segments":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}],
+// CHECK-SAME: "expansions":[],
+
+// Verify the Summary Section for the first file
+// CHECK-SAME: "summary":{
+// CHECK-SAME: "lines":{"count":1,"covered":1,"percent":100,"noncode":0},
+// CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
+// CHECK-SAME: "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}}
+
+// Close Files Array
+// CHECK-SAME: ],
+
+// Functions List
+// CHECK-SAME: "functions":[
+// CHECK-SAME: {"name":"main","count":100,"regions":[
+// CHECK-SAME: {{(\[[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\],?)+}}],
+// CHECK-SAME: "filenames":["{{[^"]+}}universal-binary.c"]
+// CHECK-SAME: }],
+
+// Full Export Summary
+// CHECK-SAME: "totals":{
+// CHECK-SAME: "lines":{"count":1,"covered":1,"percent":100,"noncode":0},
+// CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
+// CHECK-SAME: "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}
+
+// Close the export object, data array, and root object
+// CHECK-SAME: }]}
// RUN: llvm-cov show %S/Inputs/binary-formats.macho32l -instr-profile %t.profdata -filename-equivalence %s | FileCheck %s
// RUN: llvm-cov show %S/Inputs/binary-formats.macho64l -instr-profile %t.profdata -filename-equivalence %s | FileCheck %s
// RUN: llvm-cov show %S/Inputs/binary-formats.macho32b -instr-profile %t.profdata -filename-equivalence %s | FileCheck %s
+
+// RUN: llvm-cov export %S/Inputs/binary-formats.macho32l -instr-profile %t.profdata | FileCheck %S/Inputs/binary-formats.canonical.json
+// RUN: llvm-cov export %S/Inputs/binary-formats.macho64l -instr-profile %t.profdata | FileCheck %S/Inputs/binary-formats.canonical.json
+// RUN: llvm-cov export %S/Inputs/binary-formats.macho32b -instr-profile %t.profdata | FileCheck %S/Inputs/binary-formats.canonical.json
DO_SOMETHING(i); // CHECK-DAG: Expansion at line [[@LINE]], 5 -> 17
return 0;
}
+// RUN: llvm-cov export %S/Inputs/showExpansions.covmapping -instr-profile %S/Inputs/showExpansions.profdata 2>&1 | FileCheck %S/Inputs/showExpansions.json
func2(9);
return 0;
}
+
+// RUN: llvm-cov export %S/Inputs/highlightedRanges.covmapping -instr-profile %S/Inputs/highlightedRanges.profdata 2>&1 | FileCheck %S/Inputs/highlightedRanges.json
// HTML: <td class='covered-line'><pre>161</pre></td><td class='line-number'><a name='L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='code'><pre>}
// HTML-WHOLE-FILE: <td class='uncovered-line'></td><td class='line-number'><a name='L[[@LINE-44]]'><pre>[[@LINE-44]]</pre></a></td><td class='code'><pre>// after
// HTML-FILTER-NOT: <td class='uncovered-line'></td><td class='line-number'><a name='L[[@LINE-45]]'><pre>[[@LINE-45]]</pre></a></td><td class='code'><pre>// after
+
+// RUN: llvm-cov export %S/Inputs/lineExecutionCounts.covmapping -instr-profile %t.profdata -name=main 2>/dev/null | FileCheck %S/Inputs/lineExecutionCounts.json
}
// RUN: llvm-cov show %S/Inputs/regionMarkers.covmapping -instr-profile %t.profdata -show-regions -dump -filename-equivalence %s 2>&1 | FileCheck %s
+
+// RUN: llvm-cov export %S/Inputs/regionMarkers.covmapping -instr-profile %t.profdata 2>&1 | FileCheck %S/Inputs/regionMarkers.json
// RUN: llvm-profdata merge %S/Inputs/universal-binary.proftext -o %t.profdata
// RUN: llvm-cov show %S/Inputs/universal-binary -instr-profile %t.profdata -filename-equivalence %s -arch x86_64 | FileCheck %s
+// RUN: llvm-cov export %S/Inputs/universal-binary -instr-profile %t.profdata -arch x86_64 2>&1 | FileCheck %S/Inputs/universal-binary.json
+
// RUN: not llvm-cov show %S/Inputs/universal-binary -instr-profile %t.profdata -filename-equivalence %s -arch i386 2>&1 | FileCheck --check-prefix=WRONG-ARCH %s
// WRONG-ARCH: Failed to load coverage
llvm-cov.cpp
gcov.cpp
CodeCoverage.cpp
+ CoverageExporterJson.cpp
CoverageFilters.cpp
CoverageReport.cpp
CoverageSummaryInfo.cpp
using namespace llvm;
using namespace coverage;
+void exportCoverageDataToJson(StringRef ObjectFilename,
+ const coverage::CoverageMapping &CoverageMapping,
+ raw_ostream &OS);
+
namespace {
/// \brief The implementation of the coverage tool.
class CodeCoverageTool {
/// \brief The show command.
Show,
/// \brief The report command.
- Report
+ Report,
+ /// \brief The export command.
+ Export
};
/// \brief Print the error message to the error output stream.
int report(int argc, const char **argv,
CommandLineParserType commandLineParser);
+ int export_(int argc, const char **argv,
+ CommandLineParserType commandLineParser);
+
std::string ObjectFilename;
CoverageViewOptions ViewOpts;
std::string PGOFilename;
return show(argc, argv, commandLineParser);
case Report:
return report(argc, argv, commandLineParser);
+ case Export:
+ return export_(argc, argv, commandLineParser);
}
return 0;
}
return 0;
}
+int CodeCoverageTool::export_(int argc, const char **argv,
+ CommandLineParserType commandLineParser) {
+
+ auto Err = commandLineParser(argc, argv);
+ if (Err)
+ return Err;
+
+ auto Coverage = load();
+ if (!Coverage) {
+ error("Could not load coverage information");
+ return 1;
+ }
+
+ exportCoverageDataToJson(ObjectFilename, *Coverage.get(), outs());
+
+ return 0;
+}
+
int showMain(int argc, const char *argv[]) {
CodeCoverageTool Tool;
return Tool.run(CodeCoverageTool::Show, argc, argv);
CodeCoverageTool Tool;
return Tool.run(CodeCoverageTool::Report, argc, argv);
}
+
+int exportMain(int argc, const char *argv[]) {
+ CodeCoverageTool Tool;
+ return Tool.run(CodeCoverageTool::Export, argc, argv);
+}
--- /dev/null
+//===- 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
+// ------ RegionCoverage: dict => Object summarizing region coverage
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoverageSummaryInfo.h"
+#include "CoverageViewOptions.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
+#include <stack>
+
+/// \brief Major version of the JSON Coverage Export Format.
+#define LLVM_COVERAGE_EXPORT_JSON_MAJOR 1
+
+/// \brief Minor version of the JSON Coverage Export Format.
+#define LLVM_COVERAGE_EXPORT_JSON_MINOR 0
+
+/// \brief Patch version of the JSON Coverage Export Format.
+#define LLVM_COVERAGE_EXPORT_JSON_PATCH 0
+
+/// \brief The semantic version combined as a string.
+#define LLVM_COVERAGE_EXPORT_JSON_STR "1.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 A Name of the object file coverage is for.
+ StringRef ObjectFilename;
+
+ /// \brief Output stream to print JSON to.
+ raw_ostream &OS;
+
+ /// \brief The full CoverageMapping object to export.
+ 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 Get the object filename.
+ StringRef getObjectFilename() const { return ObjectFilename; }
+
+ /// \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 << "\"" << Value << "\""; }
+
+ /// \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();
+ OS << "\"" << Key << "\":";
+ 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();
+ emitDictElement("object", getObjectFilename());
+
+ emitDictKey("files");
+ FileCoverageSummary Totals = FileCoverageSummary("Totals");
+ renderFiles(Coverage.getUniqueSourceFiles(), Totals);
+
+ 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<StringRef> SourceFiles,
+ FileCoverageSummary &Summary) {
+ // Start List of Files.
+ emitArrayStart();
+ for (const auto &SourceFile : SourceFiles) {
+ // Render the file.
+ auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
+ renderFile(FileCoverage);
+
+ for (const auto &F : Coverage.getCoveredFunctions(SourceFile))
+ Summary.addFunction(FunctionCoverageSummary::get(F));
+ }
+
+ // End List of Files.
+ emitArrayEnd();
+ }
+
+ /// \brief Render a single file.
+ void renderFile(const CoverageData &FileCoverage) {
+ // 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();
+
+ FileCoverageSummary Summary =
+ FileCoverageSummary(FileCoverage.getFilename());
+ for (const auto &F :
+ Coverage.getCoveredFunctions(FileCoverage.getFilename()))
+ Summary.addFunction(FunctionCoverageSummary::get(F));
+
+ emitDictKey("summary");
+ renderSummary(Summary);
+
+ // 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());
+ emitDictElement("noncode", Summary.LineCoverage.NonCodeLines);
+ // 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("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(StringRef ObjectFilename,
+ const CoverageMapping &CoverageMapping, raw_ostream &OS)
+ : ObjectFilename(ObjectFilename), 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(StringRef ObjectFilename,
+ const CoverageMapping &CoverageMapping,
+ raw_ostream &OS) {
+ auto Exporter = CoverageExporterJson(ObjectFilename, CoverageMapping, OS);
+
+ Exporter.print();
+}
/// \brief The main entry point for the 'report' subcommand.
int reportMain(int argc, const char *argv[]);
+/// \brief The main entry point for the 'export' subcommand.
+int exportMain(int argc, const char *argv[]);
+
/// \brief The main entry point for the 'convert-for-testing' subcommand.
int convertForTestingMain(int argc, const char *argv[]);
/// \brief Top level help.
static int helpMain(int argc, const char *argv[]) {
- errs() << "Usage: llvm-cov {gcov|report|show} [OPTION]...\n\n"
+ errs() << "Usage: llvm-cov {export|gcov|report|show} [OPTION]...\n\n"
<< "Shows code coverage information.\n\n"
<< "Subcommands:\n"
+ << " export: Export instrprof file to structured format.\n"
<< " gcov: Work with the gcov format.\n"
- << " show: Annotate source files using instrprof style coverage.\n"
- << " report: Summarize instrprof style coverage information.\n";
+ << " report: Summarize instrprof style coverage information.\n"
+ << " show: Annotate source files using instrprof style coverage.\n";
+
return 0;
}
typedef int (*MainFunction)(int, const char *[]);
MainFunction Func = StringSwitch<MainFunction>(argv[1])
.Case("convert-for-testing", convertForTestingMain)
+ .Case("export", exportMain)
.Case("gcov", gcovMain)
.Case("report", reportMain)
.Case("show", showMain)