From d5b7436c1fa6b2c406c48fcae8d1cc486c3df6cf Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Tue, 26 Jul 2016 21:35:43 +0000 Subject: [PATCH] [llvm-cov] Add support for exporting coverage data to JSON This enables users to export coverage information as portable JSON for use by analysis tools and storage in document based databases. The export sub-command is invoked just like the others: llvm-cov export -instr-profile path/to/foo.profdata path/to/foo.binary The resulting JSON contains a list of files and functions. Every file object contains a list of segments, expansions, and a summary of the file's region, function, and line coverage. Every function object contains the function's name and regions. There is also a total summary for the entire object file. Patch by Eddie Hurtig! Differential Revision: https://reviews.llvm.org/D22651 llvm-svn: 276813 --- llvm/docs/CommandGuide/llvm-cov.rst | 29 ++ .../Inputs/binary-formats.canonical.json | 38 ++ .../llvm-cov/Inputs/highlightedRanges.json | 53 +++ .../llvm-cov/Inputs/lineExecutionCounts.json | 38 ++ .../tools/llvm-cov/Inputs/regionMarkers.json | 37 ++ .../tools/llvm-cov/Inputs/showExpansions.json | 51 +++ .../llvm-cov/Inputs/universal-binary.json | 36 ++ llvm/test/tools/llvm-cov/binary-formats.c | 4 + llvm/test/tools/llvm-cov/showExpansions.cpp | 1 + .../tools/llvm-cov/showHighlightedRanges.cpp | 1 + .../llvm-cov/showLineExecutionCounts.cpp | 4 + .../test/tools/llvm-cov/showRegionMarkers.cpp | 2 + llvm/test/tools/llvm-cov/universal-binary.c | 2 + llvm/tools/llvm-cov/CMakeLists.txt | 1 + llvm/tools/llvm-cov/CodeCoverage.cpp | 36 +- llvm/tools/llvm-cov/CoverageExporterJson.cpp | 418 ++++++++++++++++++ llvm/tools/llvm-cov/llvm-cov.cpp | 12 +- 17 files changed, 759 insertions(+), 4 deletions(-) create mode 100644 llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json create mode 100644 llvm/test/tools/llvm-cov/Inputs/highlightedRanges.json create mode 100644 llvm/test/tools/llvm-cov/Inputs/lineExecutionCounts.json create mode 100644 llvm/test/tools/llvm-cov/Inputs/regionMarkers.json create mode 100644 llvm/test/tools/llvm-cov/Inputs/showExpansions.json create mode 100644 llvm/test/tools/llvm-cov/Inputs/universal-binary.json create mode 100644 llvm/tools/llvm-cov/CoverageExporterJson.cpp diff --git a/llvm/docs/CommandGuide/llvm-cov.rst b/llvm/docs/CommandGuide/llvm-cov.rst index 946b125a4529f..665dbcd2113a3 100644 --- a/llvm/docs/CommandGuide/llvm-cov.rst +++ b/llvm/docs/CommandGuide/llvm-cov.rst @@ -24,6 +24,7 @@ COMMANDS * :ref:`gcov ` * :ref:`show ` * :ref:`report ` +* :ref:`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= + + 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 0000000000000..5682c5a912c17 --- /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 0000000000000..ceb6921486d71 --- /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 0000000000000..fb98f11f4998d --- /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 0000000000000..809560f35743b --- /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 0000000000000..e64e176317619 --- /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 0000000000000..b2f95abbaf656 --- /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 31c6c4c3bf4d5..cc9e4e03213f0 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 de3898fc897a0..818c0e16c57b7 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 b5209d40d22b7..1797c5252dcad 100644 --- a/llvm/test/tools/llvm-cov/showHighlightedRanges.cpp +++ b/llvm/test/tools/llvm-cov/showHighlightedRanges.cpp @@ -43,3 +43,4 @@ 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 7a33c31efe7e3..faba48d681186 100644 --- a/llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp +++ b/llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp @@ -69,3 +69,7 @@ int main() { // TEXT: 161| [[@LINE]]|int main( // 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 -name=main 2>/dev/null > %t.json
+// FileCheck -input-file=%t.json %S/Inputs/lineExecutionCounts.json
+// RUN: cat %t.json | %python -c 'import json, sys; json.loads(sys.stdin.read())'
diff --git a/llvm/test/tools/llvm-cov/showRegionMarkers.cpp b/llvm/test/tools/llvm-cov/showRegionMarkers.cpp
index 5507367c0ed1e..6c25cdbfd587b 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 0d3eb4cfa56c6..d2701ba88654a 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 e22828e11effe..d0416b06f9cde 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 e274483d31eb5..494912ed31367 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 @@ 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.
@@ -94,6 +100,9 @@ class CodeCoverageTool {
   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 0000000000000..4ce07b59f0f28
--- /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 
+
+/// \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 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 
+  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 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 ba60cd91da906..158415870250c 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(argv[1])
                             .Case("convert-for-testing", convertForTestingMain)
+                            .Case("export", exportMain)
                             .Case("gcov", gcovMain)
                             .Case("report", reportMain)
                             .Case("show", showMain)