Index: test/tools/llvm-cov/Inputs/binary-formats.canonical.json =================================================================== --- /dev/null +++ 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: }]} Index: test/tools/llvm-cov/Inputs/highlightedRanges.json =================================================================== --- /dev/null +++ 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: }]} Index: test/tools/llvm-cov/Inputs/lineExecutionCounts.json =================================================================== --- /dev/null +++ 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: }]} Index: test/tools/llvm-cov/Inputs/regionMarkers.json =================================================================== --- /dev/null +++ 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: }]} Index: test/tools/llvm-cov/Inputs/showExpansions.json =================================================================== --- /dev/null +++ test/tools/llvm-cov/Inputs/showExpansions.json @@ -0,0 +1,73 @@ +// 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: [4,3,99,1,1],[4,6,99,1,1],[5,4,99,1,0], +// CHECK-SAME: [5,12,99,1,1],[5,13,99,1,0],[5,14,0,0,0], +// CHECK-SAME: [7,3,1,1,1],[7,6,1,1,1],[8,12,0,1,1], +// CHECK-SAME: [9,6,1,1,0],[10,4,1,1,0],[10,12,1,1,1], +// CHECK-SAME: [10,13,1,1,0],[10,14,0,0,0],[13,3,100,1,1], +// CHECK-SAME: [13,6,100,1,1],[15,7,99,1,1],[15,24,100,1,0], +// CHECK-SAME: [17,7,1,1,1],[17,20,100,1,0],[18,4,100,1,0], +// CHECK-SAME: [18,12,100,1,1],[18,13,100,1,0],[18,14,0,0,0], +// CHECK-SAME: [22,40,1,1,1],[23,19,101,1,1],[23,26,1,1,0], +// CHECK-SAME: [23,28,100,1,1],[23,31,1,1,0],[24,5,100,1,1], +// CHECK-SAME: [24,17,1,1,0],[26,2,0,0,0]], +// CHECK-SAME: "expansions":[ +// CHECK-SAME: {"source_region":[24,5,24,17,100,0,1,1], +// CHECK-SAME: "target_regions":[ +// CHECK-SAME: [22,40,26,2,1,0,0,0],[23,19,23,26,101,0,0,0], +// CHECK-SAME: [23,28,23,31,100,0,0,0],[24,5,24,17,100,0,1,1], +// CHECK-SAME: [13,3,18,14,100,1,0,0],[13,6,18,4,100,1,0,0], +// CHECK-SAME: [15,7,15,24,99,1,2,1],[17,7,17,20,1,1,3,1], +// CHECK-SAME: [18,12,18,13,100,1,0,0],[4,3,5,14,99,2,0,0], +// CHECK-SAME: [4,6,5,4,99,2,0,0],[5,12,5,13,99,2,0,0], +// CHECK-SAME: [7,3,10,14,1,3,0,0],[7,6,10,4,1,3,0,0], +// CHECK-SAME: [8,12,9,6,0,3,0,0],[10,12,10,13,1,3,0,0]], + +// 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: [22,40,26,2,1,0,0,0],[23,19,23,26,101,0,0,0], +// CHECK-SAME: [23,28,23,31,100,0,0,0],[24,5,24,17,100,0,1,1], +// CHECK-SAME: [13,3,18,14,100,1,0,0],[13,6,18,4,100,1,0,0], +// CHECK-SAME: [15,7,15,24,99,1,2,1],[17,7,17,20,1,1,3,1], +// CHECK-SAME: [18,12,18,13,100,1,0,0],[4,3,5,14,99,2,0,0], +// CHECK-SAME: [4,6,5,4,99,2,0,0],[5,12,5,13,99,2,0,0], +// CHECK-SAME: [7,3,10,14,1,3,0,0],[7,6,10,4,1,3,0,0], +// CHECK-SAME: [8,12,9,6,0,3,0,0],[10,12,10,13,1,3,0,0]], +// 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: }]} Index: test/tools/llvm-cov/Inputs/universal-binary.json =================================================================== --- /dev/null +++ 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: }]} Index: test/tools/llvm-cov/binary-formats.c =================================================================== --- test/tools/llvm-cov/binary-formats.c +++ test/tools/llvm-cov/binary-formats.c @@ -7,3 +7,7 @@ // 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 -filename-equivalence %s | FileCheck %S/Inputs/binary-formats.canonical.json +// RUN: llvm-cov export %S/Inputs/binary-formats.macho64l -instr-profile %t.profdata -filename-equivalence %s | FileCheck %S/Inputs/binary-formats.canonical.json +// RUN: llvm-cov export %S/Inputs/binary-formats.macho32b -instr-profile %t.profdata -filename-equivalence %s | FileCheck %S/Inputs/binary-formats.canonical.json Index: test/tools/llvm-cov/showExpansions.cpp =================================================================== --- test/tools/llvm-cov/showExpansions.cpp +++ test/tools/llvm-cov/showExpansions.cpp @@ -24,3 +24,4 @@ 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 -filename-equivalence %s 2>&1 | FileCheck %S/Inputs/showExpansions.json Index: test/tools/llvm-cov/showHighlightedRanges.cpp =================================================================== --- test/tools/llvm-cov/showHighlightedRanges.cpp +++ test/tools/llvm-cov/showHighlightedRanges.cpp @@ -43,3 +43,4 @@ func2(9); return 0; } +// RUN: llvm-cov export %S/Inputs/highlightedRanges.covmapping -instr-profile %S/Inputs/highlightedRanges.profdata -filename-equivalence %s 2>&1 | FileCheck %S/Inputs/highlightedRanges.json Index: test/tools/llvm-cov/showLineExecutionCounts.cpp =================================================================== --- test/tools/llvm-cov/showLineExecutionCounts.cpp +++ test/tools/llvm-cov/showLineExecutionCounts.cpp @@ -69,3 +69,5 @@ // HTML:
161
[[@LINE-44]]
} // HTML-WHOLE-FILE: [[@LINE-44]] // after // HTML-FILTER-NOT: [[@LINE-45]] // after + +// RUN: llvm-cov export %S/Inputs/lineExecutionCounts.covmapping -instr-profile %t.profdata -filename-equivalence -name=main %s | FileCheck %S/Inputs/lineExecutionCounts.json Index: test/tools/llvm-cov/showRegionMarkers.cpp =================================================================== --- test/tools/llvm-cov/showRegionMarkers.cpp +++ test/tools/llvm-cov/showRegionMarkers.cpp @@ -23,3 +23,5 @@ } // 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 -filename-equivalence %s 2>&1 %s | FileCheck %S/Inputs/regionMarkers.json Index: test/tools/llvm-cov/universal-binary.c =================================================================== --- test/tools/llvm-cov/universal-binary.c +++ test/tools/llvm-cov/universal-binary.c @@ -5,6 +5,8 @@ // 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 -filename-equivalence %s -arch x86_64 | 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 Index: tools/llvm-cov/CMakeLists.txt =================================================================== --- tools/llvm-cov/CMakeLists.txt +++ tools/llvm-cov/CMakeLists.txt @@ -4,6 +4,7 @@ llvm-cov.cpp gcov.cpp CodeCoverage.cpp + CoverageExporterJson.cpp CoverageFilters.cpp CoverageReport.cpp CoverageSummaryInfo.cpp Index: tools/llvm-cov/CodeCoverage.cpp =================================================================== --- tools/llvm-cov/CodeCoverage.cpp +++ tools/llvm-cov/CodeCoverage.cpp @@ -38,6 +38,11 @@ using namespace llvm; using namespace coverage; +void exportCoverageDataToJson(StringRef ObjectFilename, + const CoverageViewOptions &Options, + const coverage::CoverageMapping &CoverageMapping, + raw_ostream &OS); + namespace { /// \brief The implementation of the coverage tool. class CodeCoverageTool { @@ -46,7 +51,9 @@ /// \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 +101,9 @@ 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 +544,8 @@ return show(argc, argv, commandLineParser); case Report: return report(argc, argv, commandLineParser); + case Export: + return export_(argc, argv, commandLineParser); } return 0; } @@ -694,6 +706,24 @@ 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, ViewOpts, *Coverage.get(), outs()); + + return 0; +} + int showMain(int argc, const char *argv[]) { CodeCoverageTool Tool; return Tool.run(CodeCoverageTool::Show, argc, argv); @@ -703,3 +733,8 @@ 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); +} Index: tools/llvm-cov/CoverageExporterJson.cpp =================================================================== --- /dev/null +++ tools/llvm-cov/CoverageExporterJson.cpp @@ -0,0 +1,428 @@ +//===- 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 a code coverage 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+ +/// \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 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 Various options to guide the coverage renderer. + const CoverageViewOptions &Options; + + /// \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 State; + + /// \brief Get the object filename. + StringRef getObjectFilename() const { return ObjectFilename; } + + /// \brief Retrieve the CoverageViewOptions. + const CoverageViewOptions &getOptions() const { return Options; } + + /// \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 + 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 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 &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 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 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 CoverageViewOptions &Options, + const CoverageMapping &CoverageMapping, raw_ostream &OS) + : ObjectFilename(ObjectFilename), 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(StringRef ObjectFilename, + const CoverageViewOptions &Options, + const CoverageMapping &CoverageMapping, + raw_ostream &OS) { + auto Exporter = + CoverageExporterJson(ObjectFilename, Options, CoverageMapping, OS); + + Exporter.print(); +} Index: tools/llvm-cov/llvm-cov.cpp =================================================================== --- tools/llvm-cov/llvm-cov.cpp +++ tools/llvm-cov/llvm-cov.cpp @@ -30,6 +30,9 @@ /// \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 @@ /// \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 @@ typedef int (*MainFunction)(int, const char *[]); MainFunction Func = StringSwitch (argv[1]) .Case("convert-for-testing", convertForTestingMain) + .Case("export", exportMain) .Case("gcov", gcovMain) .Case("report", reportMain) .Case("show", showMain)