diff options
Diffstat (limited to 'llvm')
-rw-r--r-- | llvm/docs/CommandGuide/llvm-cov.rst | 29 | ||||
-rw-r--r-- | llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json | 38 | ||||
-rw-r--r-- | llvm/test/tools/llvm-cov/Inputs/highlightedRanges.json | 53 | ||||
-rw-r--r-- | llvm/test/tools/llvm-cov/Inputs/lineExecutionCounts.json | 38 | ||||
-rw-r--r-- | llvm/test/tools/llvm-cov/Inputs/regionMarkers.json | 37 | ||||
-rw-r--r-- | llvm/test/tools/llvm-cov/Inputs/showExpansions.json | 51 | ||||
-rw-r--r-- | llvm/test/tools/llvm-cov/Inputs/universal-binary.json | 36 | ||||
-rw-r--r-- | llvm/test/tools/llvm-cov/binary-formats.c | 4 | ||||
-rw-r--r-- | llvm/test/tools/llvm-cov/showExpansions.cpp | 1 | ||||
-rw-r--r-- | llvm/test/tools/llvm-cov/showHighlightedRanges.cpp | 2 | ||||
-rw-r--r-- | llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp | 2 | ||||
-rw-r--r-- | llvm/test/tools/llvm-cov/showRegionMarkers.cpp | 2 | ||||
-rw-r--r-- | llvm/test/tools/llvm-cov/universal-binary.c | 2 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/CMakeLists.txt | 1 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/CodeCoverage.cpp | 36 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/CoverageExporterJson.cpp | 418 | ||||
-rw-r--r-- | llvm/tools/llvm-cov/llvm-cov.cpp | 12 |
17 files changed, 758 insertions, 4 deletions
diff --git a/llvm/docs/CommandGuide/llvm-cov.rst b/llvm/docs/CommandGuide/llvm-cov.rst index 946b125..665dbcd 100644 --- a/llvm/docs/CommandGuide/llvm-cov.rst +++ b/llvm/docs/CommandGuide/llvm-cov.rst @@ -24,6 +24,7 @@ COMMANDS * :ref:`gcov <llvm-cov-gcov>` * :ref:`show <llvm-cov-show>` * :ref:`report <llvm-cov-report>` +* :ref:`export <llvm-cov-export>` .. program:: llvm-cov gcov @@ -315,3 +316,31 @@ OPTIONS 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. diff --git a/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json b/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json new file mode 100644 index 0000000..cd2b052 --- /dev/null +++ b/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json @@ -0,0 +1,38 @@ +// 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: }]} diff --git a/llvm/test/tools/llvm-cov/Inputs/highlightedRanges.json b/llvm/test/tools/llvm-cov/Inputs/highlightedRanges.json new file mode 100644 index 0000000..4b441ab --- /dev/null +++ b/llvm/test/tools/llvm-cov/Inputs/highlightedRanges.json @@ -0,0 +1,53 @@ +// 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: }]} diff --git a/llvm/test/tools/llvm-cov/Inputs/lineExecutionCounts.json b/llvm/test/tools/llvm-cov/Inputs/lineExecutionCounts.json new file mode 100644 index 0000000..4beab24 --- /dev/null +++ b/llvm/test/tools/llvm-cov/Inputs/lineExecutionCounts.json @@ -0,0 +1,38 @@ +// 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: }]} diff --git a/llvm/test/tools/llvm-cov/Inputs/regionMarkers.json b/llvm/test/tools/llvm-cov/Inputs/regionMarkers.json new file mode 100644 index 0000000..7565c9f --- /dev/null +++ b/llvm/test/tools/llvm-cov/Inputs/regionMarkers.json @@ -0,0 +1,37 @@ +// 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: }]} diff --git a/llvm/test/tools/llvm-cov/Inputs/showExpansions.json b/llvm/test/tools/llvm-cov/Inputs/showExpansions.json new file mode 100644 index 0000000..738c5e1 --- /dev/null +++ b/llvm/test/tools/llvm-cov/Inputs/showExpansions.json @@ -0,0 +1,51 @@ +// 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: }]} diff --git a/llvm/test/tools/llvm-cov/Inputs/universal-binary.json b/llvm/test/tools/llvm-cov/Inputs/universal-binary.json new file mode 100644 index 0000000..75d7a2f --- /dev/null +++ b/llvm/test/tools/llvm-cov/Inputs/universal-binary.json @@ -0,0 +1,36 @@ +// 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: }]} diff --git a/llvm/test/tools/llvm-cov/binary-formats.c b/llvm/test/tools/llvm-cov/binary-formats.c index 31c6c4c..cc9e4e0 100644 --- a/llvm/test/tools/llvm-cov/binary-formats.c +++ b/llvm/test/tools/llvm-cov/binary-formats.c @@ -7,3 +7,7 @@ int main(int argc, const char *argv[]) {} // 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 diff --git a/llvm/test/tools/llvm-cov/showExpansions.cpp b/llvm/test/tools/llvm-cov/showExpansions.cpp index de3898f..818c0e1 100644 --- a/llvm/test/tools/llvm-cov/showExpansions.cpp +++ b/llvm/test/tools/llvm-cov/showExpansions.cpp @@ -24,3 +24,4 @@ int main(int argc, const char *argv[]) { 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 diff --git a/llvm/test/tools/llvm-cov/showHighlightedRanges.cpp b/llvm/test/tools/llvm-cov/showHighlightedRanges.cpp index b5209d40..dddbc2b 100644 --- a/llvm/test/tools/llvm-cov/showHighlightedRanges.cpp +++ b/llvm/test/tools/llvm-cov/showHighlightedRanges.cpp @@ -43,3 +43,5 @@ int main() { 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 diff --git a/llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp b/llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp index 7a33c31..f754052 100644 --- a/llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp +++ b/llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp @@ -69,3 +69,5 @@ int main() { // TEXT: 161| [[@LINE]]|int main( // 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 diff --git a/llvm/test/tools/llvm-cov/showRegionMarkers.cpp b/llvm/test/tools/llvm-cov/showRegionMarkers.cpp index 5507367..6c25cdb 100644 --- a/llvm/test/tools/llvm-cov/showRegionMarkers.cpp +++ b/llvm/test/tools/llvm-cov/showRegionMarkers.cpp @@ -23,3 +23,5 @@ int main() { // CHECK: Marker at [[@LINE]]:12 = 1.11M } // 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 diff --git a/llvm/test/tools/llvm-cov/universal-binary.c b/llvm/test/tools/llvm-cov/universal-binary.c index 0d3eb4cf..d2701ba 100644 --- a/llvm/test/tools/llvm-cov/universal-binary.c +++ b/llvm/test/tools/llvm-cov/universal-binary.c @@ -5,6 +5,8 @@ int main(int argc, const char *argv[]) {} // 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 diff --git a/llvm/tools/llvm-cov/CMakeLists.txt b/llvm/tools/llvm-cov/CMakeLists.txt index e22828e..d0416b0 100644 --- a/llvm/tools/llvm-cov/CMakeLists.txt +++ b/llvm/tools/llvm-cov/CMakeLists.txt @@ -4,6 +4,7 @@ add_llvm_tool(llvm-cov llvm-cov.cpp gcov.cpp CodeCoverage.cpp + CoverageExporterJson.cpp CoverageFilters.cpp CoverageReport.cpp CoverageSummaryInfo.cpp diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp index e274483..494912e 100644 --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -38,6 +38,10 @@ 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 { @@ -46,7 +50,9 @@ public: /// \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. @@ -94,6 +100,9 @@ public: 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; @@ -534,6 +543,8 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { return show(argc, argv, commandLineParser); case Report: return report(argc, argv, commandLineParser); + case Export: + return export_(argc, argv, commandLineParser); } return 0; } @@ -694,6 +705,24 @@ int CodeCoverageTool::report(int argc, const char **argv, 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); @@ -703,3 +732,8 @@ int reportMain(int argc, const char *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); +} diff --git a/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/llvm/tools/llvm-cov/CoverageExporterJson.cpp new file mode 100644 index 0000000..4ce07b5 --- /dev/null +++ b/llvm/tools/llvm-cov/CoverageExporterJson.cpp @@ -0,0 +1,418 @@ +//===- 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(); +} diff --git a/llvm/tools/llvm-cov/llvm-cov.cpp b/llvm/tools/llvm-cov/llvm-cov.cpp index ba60cd9..1584158 100644 --- a/llvm/tools/llvm-cov/llvm-cov.cpp +++ b/llvm/tools/llvm-cov/llvm-cov.cpp @@ -30,6 +30,9 @@ int showMain(int argc, const char *argv[]); /// \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[]); @@ -38,12 +41,14 @@ int gcovMain(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; } @@ -68,6 +73,7 @@ int main(int argc, const char **argv) { 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) |