diff --git a/llvm/docs/CommandGuide/llvm-cov.rst b/llvm/docs/CommandGuide/llvm-cov.rst index 946b125a4529..665dbcd2113a 100644 --- a/llvm/docs/CommandGuide/llvm-cov.rst +++ b/llvm/docs/CommandGuide/llvm-cov.rst @@ -1,317 +1,346 @@ llvm-cov - emit coverage information ==================================== SYNOPSIS -------- :program:`llvm-cov` *command* [*args...*] DESCRIPTION ----------- The :program:`llvm-cov` tool shows code coverage information for programs that are instrumented to emit profile data. It can be used to work with ``gcov``\-style coverage or with ``clang``\'s instrumentation based profiling. If the program is invoked with a base name of ``gcov``, it will behave as if the :program:`llvm-cov gcov` command were called. Otherwise, a command should be provided. COMMANDS -------- * :ref:`gcov ` * :ref:`show ` * :ref:`report ` +* :ref:`export ` .. program:: llvm-cov gcov .. _llvm-cov-gcov: GCOV COMMAND ------------ SYNOPSIS ^^^^^^^^ :program:`llvm-cov gcov` [*options*] *SOURCEFILE* DESCRIPTION ^^^^^^^^^^^ The :program:`llvm-cov gcov` tool reads code coverage data files and displays the coverage information for a specified source file. It is compatible with the ``gcov`` tool from version 4.2 of ``GCC`` and may also be compatible with some later versions of ``gcov``. To use :program:`llvm-cov gcov`, you must first build an instrumented version of your application that collects coverage data as it runs. Compile with the ``-fprofile-arcs`` and ``-ftest-coverage`` options to add the instrumentation. (Alternatively, you can use the ``--coverage`` option, which includes both of those other options.) You should compile with debugging information (``-g``) and without optimization (``-O0``); otherwise, the coverage data cannot be accurately mapped back to the source code. At the time you compile the instrumented code, a ``.gcno`` data file will be generated for each object file. These ``.gcno`` files contain half of the coverage data. The other half of the data comes from ``.gcda`` files that are generated when you run the instrumented program, with a separate ``.gcda`` file for each object file. Each time you run the program, the execution counts are summed into any existing ``.gcda`` files, so be sure to remove any old files if you do not want their contents to be included. By default, the ``.gcda`` files are written into the same directory as the object files, but you can override that by setting the ``GCOV_PREFIX`` and ``GCOV_PREFIX_STRIP`` environment variables. The ``GCOV_PREFIX_STRIP`` variable specifies a number of directory components to be removed from the start of the absolute path to the object file directory. After stripping those directories, the prefix from the ``GCOV_PREFIX`` variable is added. These environment variables allow you to run the instrumented program on a machine where the original object file directories are not accessible, but you will then need to copy the ``.gcda`` files back to the object file directories where :program:`llvm-cov gcov` expects to find them. Once you have generated the coverage data files, run :program:`llvm-cov gcov` for each main source file where you want to examine the coverage results. This should be run from the same directory where you previously ran the compiler. The results for the specified source file are written to a file named by appending a ``.gcov`` suffix. A separate output file is also created for each file included by the main source file, also with a ``.gcov`` suffix added. The basic content of an ``.gcov`` output file is a copy of the source file with an execution count and line number prepended to every line. The execution count is shown as ``-`` if a line does not contain any executable code. If a line contains code but that code was never executed, the count is displayed as ``#####``. OPTIONS ^^^^^^^ .. option:: -a, --all-blocks Display all basic blocks. If there are multiple blocks for a single line of source code, this option causes llvm-cov to show the count for each block instead of just one count for the entire line. .. option:: -b, --branch-probabilities Display conditional branch probabilities and a summary of branch information. .. option:: -c, --branch-counts Display branch counts instead of probabilities (requires -b). .. option:: -f, --function-summaries Show a summary of coverage for each function instead of just one summary for an entire source file. .. option:: --help Display available options (--help-hidden for more). .. option:: -l, --long-file-names For coverage output of files included from the main source file, add the main file name followed by ``##`` as a prefix to the output file names. This can be combined with the --preserve-paths option to use complete paths for both the main file and the included file. .. option:: -n, --no-output Do not output any ``.gcov`` files. Summary information is still displayed. .. option:: -o=, --object-directory=, --object-file= Find objects in DIR or based on FILE's path. If you specify a particular object file, the coverage data files are expected to have the same base name with ``.gcno`` and ``.gcda`` extensions. If you specify a directory, the files are expected in that directory with the same base name as the source file. .. option:: -p, --preserve-paths Preserve path components when naming the coverage output files. In addition to the source file name, include the directories from the path to that file. The directories are separate by ``#`` characters, with ``.`` directories removed and ``..`` directories replaced by ``^`` characters. When used with the --long-file-names option, this applies to both the main file name and the included file name. .. option:: -u, --unconditional-branches Include unconditional branches in the output for the --branch-probabilities option. .. option:: -version Display the version of llvm-cov. EXIT STATUS ^^^^^^^^^^^ :program:`llvm-cov gcov` returns 1 if it cannot read input files. Otherwise, it exits with zero. .. program:: llvm-cov show .. _llvm-cov-show: SHOW COMMAND ------------ SYNOPSIS ^^^^^^^^ :program:`llvm-cov show` [*options*] -instr-profile *PROFILE* *BIN* [*SOURCES*] DESCRIPTION ^^^^^^^^^^^ The :program:`llvm-cov show` command shows line by line coverage of a binary *BIN* using the profile data *PROFILE*. It can optionally be filtered to only show the coverage for the files listed in *SOURCES*. To use :program:`llvm-cov show`, you need a program that is compiled with instrumentation to emit profile and coverage data. To build such a program with ``clang`` use the ``-fprofile-instr-generate`` and ``-fcoverage-mapping`` flags. If linking with the ``clang`` driver, pass ``-fprofile-instr-generate`` to the link stage to make sure the necessary runtime libraries are linked in. The coverage information is stored in the built executable or library itself, and this is what you should pass to :program:`llvm-cov show` as the *BIN* argument. The profile data is generated by running this instrumented program normally. When the program exits it will write out a raw profile file, typically called ``default.profraw``, which can be converted to a format that is suitable for the *PROFILE* argument using the :program:`llvm-profdata merge` tool. OPTIONS ^^^^^^^ .. option:: -show-line-counts Show the execution counts for each line. This is enabled by default, unless another ``-show`` option is used. .. option:: -show-expansions Expand inclusions, such as preprocessor macros or textual inclusions, inline in the display of the source file. .. option:: -show-instantiations For source regions that are instantiated multiple times, such as templates in ``C++``, show each instantiation separately as well as the combined summary. .. option:: -show-regions Show the execution counts for each region by displaying a caret that points to the character where the region starts. .. option:: -show-line-counts-or-regions Show the execution counts for each line if there is only one region on the line, but show the individual regions if there are multiple on the line. .. option:: -use-color[=VALUE] Enable or disable color output. By default this is autodetected. .. 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. .. option:: -name= Show code coverage only for functions with the given name. .. option:: -name-regex= Show code coverage only for functions that match the given regular expression. .. option:: -format= Use the specified output format. The supported formats are: "text", "html". .. option:: -output-dir=PATH Specify a directory to write coverage reports into. If the directory does not exist, it is created. When used in function view mode (i.e when -name or -name-regex are used to select specific functions), the report is written to PATH/functions.EXTENSION. When used in file view mode, a report for each file is written to PATH/REL_PATH_TO_FILE.EXTENSION. .. option:: -Xdemangler=| Specify a symbol demangler. This can be used to make reports more human-readable. This option can be specified multiple times to supply arguments to the demangler (e.g `-Xdemangler c++filt -Xdemangler -n` for C++). The demangler is expected to read a newline-separated list of symbols from stdin and write a newline-separated list of the same length to stdout. .. option:: -line-coverage-gt= Show code coverage only for functions with line coverage greater than the given threshold. .. option:: -line-coverage-lt= Show code coverage only for functions with line coverage less than the given threshold. .. option:: -region-coverage-gt= Show code coverage only for functions with region coverage greater than the given threshold. .. option:: -region-coverage-lt= Show code coverage only for functions with region coverage less than the given threshold. .. program:: llvm-cov report .. _llvm-cov-report: REPORT COMMAND -------------- SYNOPSIS ^^^^^^^^ :program:`llvm-cov report` [*options*] -instr-profile *PROFILE* *BIN* [*SOURCES*] DESCRIPTION ^^^^^^^^^^^ The :program:`llvm-cov report` command displays a summary of the coverage of a binary *BIN* using the profile data *PROFILE*. It can optionally be filtered to only show the coverage for the files listed in *SOURCES*. If no source files are provided, a summary line is printed for each file in the coverage data. If any files are provided, summaries are shown for each function in the listed files instead. For information on compiling programs for coverage and generating profile data, see :ref:`llvm-cov-show`. OPTIONS ^^^^^^^ .. option:: -use-color[=VALUE] Enable or disable color output. By default this is autodetected. .. 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. + +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 000000000000..5682c5a912c1 --- /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 000000000000..ceb6921486d7 --- /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 000000000000..fb98f11f4998 --- /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 000000000000..809560f35743 --- /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 000000000000..e64e17631761 --- /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 000000000000..b2f95abbaf65 --- /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 31c6c4c3bf4d..cc9e4e03213f 100644 --- a/llvm/test/tools/llvm-cov/binary-formats.c +++ b/llvm/test/tools/llvm-cov/binary-formats.c @@ -1,9 +1,13 @@ // Checks for reading various formats. // CHECK: 100| [[@LINE+1]]|int main int main(int argc, const char *argv[]) {} // RUN: llvm-profdata merge %S/Inputs/binary-formats.proftext -o %t.profdata // 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 de3898fc897a..818c0e16c57b 100644 --- a/llvm/test/tools/llvm-cov/showExpansions.cpp +++ b/llvm/test/tools/llvm-cov/showExpansions.cpp @@ -1,26 +1,27 @@ // RUN: llvm-cov show %S/Inputs/showExpansions.covmapping -instr-profile %S/Inputs/showExpansions.profdata -dump -show-expansions -filename-equivalence %s 2>&1 | FileCheck %s #define DO_SOMETHING_ELSE() \ do { \ } while (0) #define ANOTHER_THING() \ do { \ if (0) { \ } \ } while (0) #define DO_SOMETHING(x) \ do { \ if (x) \ DO_SOMETHING_ELSE(); \ else \ ANOTHER_THING(); \ } while (0) // CHECK-DAG: Expansion at line [[@LINE-4]], 7 -> 24 // CHECK-DAG: Expansion at line [[@LINE-3]], 7 -> 20 int main(int argc, const char *argv[]) { for (int i = 0; i < 100; ++i) 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 b5209d40d22b..1797c5252dca 100644 --- a/llvm/test/tools/llvm-cov/showHighlightedRanges.cpp +++ b/llvm/test/tools/llvm-cov/showHighlightedRanges.cpp @@ -1,45 +1,46 @@ // RUN: llvm-cov show %S/Inputs/highlightedRanges.covmapping -instr-profile %S/Inputs/highlightedRanges.profdata -dump -filename-equivalence %s 2>&1 | FileCheck %s void func() { return; int i = 0; // CHECK: Highlighted line [[@LINE]], 3 -> ? } // CHECK: Highlighted line [[@LINE]], 1 -> 2 void func2(int x) { if(x > 5) { while(x >= 9) { return; --x; // CHECK: Highlighted line [[@LINE]], 7 -> ? } // CHECK: Highlighted line [[@LINE]], 1 -> 6 int i = 0; // CHECK: Highlighted line [[@LINE]], 5 -> ? } // CHECK: Highlighted line [[@LINE]], 1 -> 4 } void test() { int x = 0; if (x) { // CHECK: Highlighted line [[@LINE]], 10 -> ? x = 0; // CHECK: Highlighted line [[@LINE]], 1 -> ? } else { // CHECK: Highlighted line [[@LINE]], 1 -> 4 x = 1; } // CHECK: Highlighted line [[@LINE+1]], 26 -> 29 for (int i = 0; i < 0; ++i) { // CHECK: Highlighted line [[@LINE]], 31 -> ? x = 1; // CHECK: Highlighted line [[@LINE]], 1 -> ? } // CHECK: Highlighted line [[@LINE]], 1 -> 4 x = x < 10 ? x + 1 : x - 1; // CHECK: Highlighted line [[@LINE]], 16 -> 21 x = x > 10 ? x + // CHECK: Highlighted line [[@LINE]], 16 -> ? 1 // CHECK: Highlighted line [[@LINE]], 1 -> 17 : x - 1; } int main() { test(); func(); 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 7a33c31efe7e..faba48d68118 100644 --- a/llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp +++ b/llvm/test/tools/llvm-cov/showLineExecutionCounts.cpp @@ -1,71 +1,75 @@ // Basic handling of line counts. // RUN: llvm-profdata merge %S/Inputs/lineExecutionCounts.proftext -o %t.profdata // before any coverage // WHOLE-FILE: | [[@LINE]]|// before // FILTER-NOT: | [[@LINE-1]]|// before int main() { // TEXT: 161| [[@LINE]]|int main( int x = 0; // TEXT: 161| [[@LINE]]| int x // TEXT: 161| [[@LINE]]| if (x) { // TEXT: 0| [[@LINE]]| if (x) x = 0; // TEXT: 0| [[@LINE]]| x = 0 } else { // TEXT: 161| [[@LINE]]| } else x = 1; // TEXT: 161| [[@LINE]]| x = 1 } // TEXT: 161| [[@LINE]]| } // TEXT: 161| [[@LINE]]| for (int i = 0; i < 100; ++i) { // TEXT: 16.2k| [[@LINE]]| for ( x = 1; // TEXT: 16.1k| [[@LINE]]| x = 1 } // TEXT: 16.1k| [[@LINE]]| } // TEXT: 161| [[@LINE]]| x = x < 10 ? x + 1 : x - 1; // TEXT: 161| [[@LINE]]| x = x = x > 10 ? // TEXT: 161| [[@LINE]]| x = x - 1: // TEXT: 0| [[@LINE]]| x x + 1; // TEXT: 161| [[@LINE]]| x // TEXT: 161| [[@LINE]]| return 0; // TEXT: 161| [[@LINE]]| return } // TEXT: 161| [[@LINE]]|} // after coverage // WHOLE-FILE: | [[@LINE]]|// after // FILTER-NOT: | [[@LINE-1]]|// after // RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -instr-profile %t.profdata -filename-equivalence %s | FileCheck -check-prefixes=TEXT,WHOLE-FILE %s // RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -instr-profile %t.profdata -filename-equivalence -name=main %s | FileCheck -check-prefixes=TEXT,FILTER %s // Test -output-dir. // RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -o %t.dir -instr-profile %t.profdata -filename-equivalence %s // RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -output-dir %t.dir -instr-profile %t.profdata -filename-equivalence -name=main %s // RUN: FileCheck -check-prefixes=TEXT,WHOLE-FILE -input-file %t.dir/coverage/tmp/showLineExecutionCounts.cpp.txt %s // RUN: FileCheck -check-prefixes=TEXT,FILTER -input-file %t.dir/functions.txt %s // // Test index creation. // RUN: FileCheck -check-prefix=INDEX -input-file %t.dir/index.txt %s // INDEX: showLineExecutionCounts.cpp.txt // // Test html output. // RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -format html -o %t.html.dir -instr-profile %t.profdata -filename-equivalence %s // RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -format html -o %t.html.dir -instr-profile %t.profdata -filename-equivalence -name=main %s // RUN: FileCheck -check-prefixes=HTML,HTML-WHOLE-FILE -input-file %t.html.dir/coverage/tmp/showLineExecutionCounts.cpp.html %s // RUN: FileCheck -check-prefixes=HTML,HTML-FILTER -input-file %t.html.dir/functions.html %s // // HTML-WHOLE-FILE:
[[@LINE-44]]
// before
 // HTML-FILTER-NOT: 
[[@LINE-45]]
// before
 // HTML: 
161
[[@LINE-44]]
int main() {
 // HTML: 
161
[[@LINE-44]]
  int x = 0
 // HTML: 
161
[[@LINE-44]]
 // HTML: 
0
[[@LINE-44]]
  if (x) {
 // HTML: 
0
[[@LINE-44]]
 // HTML: 
161
[[@LINE-44]]
  }
 // HTML: 
161
[[@LINE-44]]
    x = 1;
 // HTML: 
161
[[@LINE-44]]
  }
 // HTML: 
161
[[@LINE-44]]
 // HTML: 
16.2k
[[@LINE-44]]
  for (int i = 0; i < 100; ++i)
 // HTML: 
16.1k
[[@LINE-44]]
    x = 1;
 // HTML: 
16.1k
[[@LINE-44]]
  }
 // HTML: 
161
[[@LINE-44]]
 // HTML: 
161
[[@LINE-44]]
  x = x < 10
 // HTML: 
161
[[@LINE-44]]
  x = x > 10
 // HTML: 
0
[[@LINE-44]]
        x - 1:
 // HTML: 
161
[[@LINE-44]]
        x + 1;
 // HTML: 
161
[[@LINE-44]]
 // HTML: 
161
[[@LINE-44]]
  return 0;
 // 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 5507367c0ed1..6c25cdbfd587 100644
--- a/llvm/test/tools/llvm-cov/showRegionMarkers.cpp
+++ b/llvm/test/tools/llvm-cov/showRegionMarkers.cpp
@@ -1,25 +1,27 @@
 // RUN: llvm-profdata merge %S/Inputs/regionMarkers.proftext -o %t.profdata
 
 int main() {                      // CHECK: Marker at [[@LINE]]:12 = 1.11M
   int x = 0;
 
   if (x) {                        // CHECK: Marker at [[@LINE]]:10 = 0
     x = 0;
   } else {                        // CHECK: Marker at [[@LINE]]:10 = 1.11M
     x = 1;
   }
                                   // CHECK: Marker at [[@LINE+2]]:19 = 112M
                                   // CHECK: Marker at [[@LINE+1]]:28 = 111M
   for (int i = 0; i < 100; ++i) { // CHECK: Marker at [[@LINE]]:33 = 111M
     x = 1;
   }
                                   // CHECK: Marker at [[@LINE+1]]:16 = 1.11M
   x = x < 10 ? x + 1 : x - 1;     // CHECK: Marker at [[@LINE]]:24 = 0
   x = x > 10 ?
         x - 1:                    // CHECK: Marker at [[@LINE]]:9 = 0
         x + 1;                    // CHECK: Marker at [[@LINE]]:9 = 1.11M
 
   return 0;
 }
 
 // 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 0d3eb4cfa56c..d2701ba88654 100644
--- a/llvm/test/tools/llvm-cov/universal-binary.c
+++ b/llvm/test/tools/llvm-cov/universal-binary.c
@@ -1,13 +1,15 @@
 // The coverage reader should be able to handle universal binaries
 
 // CHECK: 100| [[@LINE+1]]|int main
 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
 
 // RUN: not llvm-cov report -instr-profile %t.profdata 2>&1 | FileCheck --check-prefix=MISSING-BINARY %s
 // MISSING-BINARY: 1 positional argument: See:
diff --git a/llvm/tools/llvm-cov/CMakeLists.txt b/llvm/tools/llvm-cov/CMakeLists.txt
index e22828e11eff..d0416b06f9cd 100644
--- a/llvm/tools/llvm-cov/CMakeLists.txt
+++ b/llvm/tools/llvm-cov/CMakeLists.txt
@@ -1,14 +1,15 @@
 set(LLVM_LINK_COMPONENTS core support object coverage profiledata)
 
 add_llvm_tool(llvm-cov
   llvm-cov.cpp
   gcov.cpp
   CodeCoverage.cpp
+  CoverageExporterJson.cpp
   CoverageFilters.cpp
   CoverageReport.cpp
   CoverageSummaryInfo.cpp
   SourceCoverageView.cpp
   SourceCoverageViewHTML.cpp
   SourceCoverageViewText.cpp
   TestingSupport.cpp
   )
diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp
index e274483d31eb..494912ed3136 100644
--- a/llvm/tools/llvm-cov/CodeCoverage.cpp
+++ b/llvm/tools/llvm-cov/CodeCoverage.cpp
@@ -1,705 +1,739 @@
 //===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
 //
 //                     The LLVM Compiler Infrastructure
 //
 // This file is distributed under the University of Illinois Open Source
 // License. See LICENSE.TXT for details.
 //
 //===----------------------------------------------------------------------===//
 //
 // The 'CodeCoverageTool' class implements a command line tool to analyze and
 // report coverage information using the profiling instrumentation and code
 // coverage mapping.
 //
 //===----------------------------------------------------------------------===//
 
 #include "CoverageFilters.h"
 #include "CoverageReport.h"
 #include "CoverageViewOptions.h"
 #include "RenderingSupport.h"
 #include "SourceCoverageView.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Triple.h"
 #include "llvm/ProfileData/Coverage/CoverageMapping.h"
 #include "llvm/ProfileData/InstrProfReader.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Format.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Process.h"
 #include "llvm/Support/Program.h"
 #include "llvm/Support/ThreadPool.h"
 #include "llvm/Support/ToolOutputFile.h"
 #include 
 #include 
 
 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 {
 public:
   enum Command {
     /// \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.
   void error(const Twine &Message, StringRef Whence = "");
 
   /// \brief Print the warning message to the error output stream.
   void warning(const Twine &Message, StringRef Whence = "");
 
   /// \brief Copy \p Path into the list of input source files.
   void addCollectedPath(const std::string &Path);
 
   /// \brief Return a memory buffer for the given source file.
   ErrorOr getSourceFile(StringRef SourceFile);
 
   /// \brief Create source views for the expansions of the view.
   void attachExpansionSubViews(SourceCoverageView &View,
                                ArrayRef Expansions,
                                const CoverageMapping &Coverage);
 
   /// \brief Create the source view of a particular function.
   std::unique_ptr
   createFunctionView(const FunctionRecord &Function,
                      const CoverageMapping &Coverage);
 
   /// \brief Create the main source view of a particular source file.
   std::unique_ptr
   createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage);
 
   /// \brief Load the coverage mapping data. Return nullptr if an error occured.
   std::unique_ptr load();
 
   /// \brief If a demangler is available, demangle all symbol names.
   void demangleSymbols(const CoverageMapping &Coverage);
 
   /// \brief Demangle \p Sym if possible. Otherwise, just return \p Sym.
   StringRef getSymbolForHumans(StringRef Sym) const;
 
   int run(Command Cmd, int argc, const char **argv);
 
   typedef llvm::function_ref CommandLineParserType;
 
   int show(int argc, const char **argv,
            CommandLineParserType commandLineParser);
 
   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;
   CoverageFiltersMatchAll Filters;
   std::vector SourceFiles;
   bool CompareFilenamesOnly;
   StringMap RemappedFilenames;
   std::string CoverageArch;
 
 private:
   /// A cache for demangled symbol names.
   StringMap DemangledNames;
 
   /// File paths (absolute, or otherwise) to input source files.
   std::vector CollectedPaths;
 
   /// Errors and warnings which have not been printed.
   std::mutex ErrsLock;
 
   /// A container for input source file buffers.
   std::mutex LoadedSourceFilesLock;
   std::vector>>
       LoadedSourceFiles;
 };
 }
 
 static std::string getErrorString(const Twine &Message, StringRef Whence,
                                   bool Warning) {
   std::string Str = (Warning ? "warning" : "error");
   Str += ": ";
   if (!Whence.empty())
     Str += Whence.str() + ": ";
   Str += Message.str() + "\n";
   return Str;
 }
 
 void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
   std::unique_lock Guard{ErrsLock};
   ViewOpts.colored_ostream(errs(), raw_ostream::RED)
       << getErrorString(Message, Whence, false);
 }
 
 void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) {
   std::unique_lock Guard{ErrsLock};
   ViewOpts.colored_ostream(errs(), raw_ostream::RED)
       << getErrorString(Message, Whence, true);
 }
 
 void CodeCoverageTool::addCollectedPath(const std::string &Path) {
   CollectedPaths.push_back(Path);
   SourceFiles.emplace_back(CollectedPaths.back());
 }
 
 ErrorOr
 CodeCoverageTool::getSourceFile(StringRef SourceFile) {
   // If we've remapped filenames, look up the real location for this file.
   std::unique_lock Guard{LoadedSourceFilesLock};
   if (!RemappedFilenames.empty()) {
     auto Loc = RemappedFilenames.find(SourceFile);
     if (Loc != RemappedFilenames.end())
       SourceFile = Loc->second;
   }
   for (const auto &Files : LoadedSourceFiles)
     if (sys::fs::equivalent(SourceFile, Files.first))
       return *Files.second;
   auto Buffer = MemoryBuffer::getFile(SourceFile);
   if (auto EC = Buffer.getError()) {
     error(EC.message(), SourceFile);
     return EC;
   }
   LoadedSourceFiles.emplace_back(SourceFile, std::move(Buffer.get()));
   return *LoadedSourceFiles.back().second;
 }
 
 void CodeCoverageTool::attachExpansionSubViews(
     SourceCoverageView &View, ArrayRef Expansions,
     const CoverageMapping &Coverage) {
   if (!ViewOpts.ShowExpandedRegions)
     return;
   for (const auto &Expansion : Expansions) {
     auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
     if (ExpansionCoverage.empty())
       continue;
     auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
     if (!SourceBuffer)
       continue;
 
     auto SubViewExpansions = ExpansionCoverage.getExpansions();
     auto SubView =
         SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
                                    ViewOpts, std::move(ExpansionCoverage));
     attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
     View.addExpansion(Expansion.Region, std::move(SubView));
   }
 }
 
 std::unique_ptr
 CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
                                      const CoverageMapping &Coverage) {
   auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
   if (FunctionCoverage.empty())
     return nullptr;
   auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
   if (!SourceBuffer)
     return nullptr;
 
   auto Expansions = FunctionCoverage.getExpansions();
   auto View = SourceCoverageView::create(getSymbolForHumans(Function.Name),
                                          SourceBuffer.get(), ViewOpts,
                                          std::move(FunctionCoverage));
   attachExpansionSubViews(*View, Expansions, Coverage);
 
   return View;
 }
 
 std::unique_ptr
 CodeCoverageTool::createSourceFileView(StringRef SourceFile,
                                        const CoverageMapping &Coverage) {
   auto SourceBuffer = getSourceFile(SourceFile);
   if (!SourceBuffer)
     return nullptr;
   auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
   if (FileCoverage.empty())
     return nullptr;
 
   auto Expansions = FileCoverage.getExpansions();
   auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
                                          ViewOpts, std::move(FileCoverage));
   attachExpansionSubViews(*View, Expansions, Coverage);
 
   for (const auto *Function : Coverage.getInstantiations(SourceFile)) {
     auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
     auto SubViewExpansions = SubViewCoverage.getExpansions();
     auto SubView = SourceCoverageView::create(
         getSymbolForHumans(Function->Name), SourceBuffer.get(), ViewOpts,
         std::move(SubViewCoverage));
     attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
 
     if (SubView) {
       unsigned FileID = Function->CountedRegions.front().FileID;
       unsigned Line = 0;
       for (const auto &CR : Function->CountedRegions)
         if (CR.FileID == FileID)
           Line = std::max(CR.LineEnd, Line);
       View->addInstantiation(Function->Name, Line, std::move(SubView));
     }
   }
   return View;
 }
 
 static bool modifiedTimeGT(StringRef LHS, StringRef RHS) {
   sys::fs::file_status Status;
   if (sys::fs::status(LHS, Status))
     return false;
   auto LHSTime = Status.getLastModificationTime();
   if (sys::fs::status(RHS, Status))
     return false;
   auto RHSTime = Status.getLastModificationTime();
   return LHSTime > RHSTime;
 }
 
 std::unique_ptr CodeCoverageTool::load() {
   if (modifiedTimeGT(ObjectFilename, PGOFilename))
     warning("profile data may be out of date - object is newer",
             ObjectFilename);
   auto CoverageOrErr =
       CoverageMapping::load(ObjectFilename, PGOFilename, CoverageArch);
   if (Error E = CoverageOrErr.takeError()) {
     error("Failed to load coverage: " + toString(std::move(E)), ObjectFilename);
     return nullptr;
   }
   auto Coverage = std::move(CoverageOrErr.get());
   unsigned Mismatched = Coverage->getMismatchedCount();
   if (Mismatched)
     warning(utostr(Mismatched) + " functions have mismatched data");
 
   if (CompareFilenamesOnly) {
     auto CoveredFiles = Coverage.get()->getUniqueSourceFiles();
     for (auto &SF : SourceFiles) {
       StringRef SFBase = sys::path::filename(SF);
       for (const auto &CF : CoveredFiles)
         if (SFBase == sys::path::filename(CF)) {
           RemappedFilenames[CF] = SF;
           SF = CF;
           break;
         }
     }
   }
 
   demangleSymbols(*Coverage);
 
   return Coverage;
 }
 
 void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) {
   if (!ViewOpts.hasDemangler())
     return;
 
   // Pass function names to the demangler in a temporary file.
   int InputFD;
   SmallString<256> InputPath;
   std::error_code EC =
       sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath);
   if (EC) {
     error(InputPath, EC.message());
     return;
   }
   tool_output_file InputTOF{InputPath, InputFD};
 
   unsigned NumSymbols = 0;
   for (const auto &Function : Coverage.getCoveredFunctions()) {
     InputTOF.os() << Function.Name << '\n';
     ++NumSymbols;
   }
   InputTOF.os().close();
 
   // Use another temporary file to store the demangler's output.
   int OutputFD;
   SmallString<256> OutputPath;
   EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD,
                                     OutputPath);
   if (EC) {
     error(OutputPath, EC.message());
     return;
   }
   tool_output_file OutputTOF{OutputPath, OutputFD};
   OutputTOF.os().close();
 
   // Invoke the demangler.
   std::vector ArgsV;
   for (const std::string &Arg : ViewOpts.DemanglerOpts)
     ArgsV.push_back(Arg.c_str());
   ArgsV.push_back(nullptr);
   StringRef InputPathRef = InputPath.str();
   StringRef OutputPathRef = OutputPath.str();
   StringRef StderrRef;
   const StringRef *Redirects[] = {&InputPathRef, &OutputPathRef, &StderrRef};
   std::string ErrMsg;
   int RC = sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV.data(),
                                /*env=*/nullptr, Redirects, /*secondsToWait=*/0,
                                /*memoryLimit=*/0, &ErrMsg);
   if (RC) {
     error(ErrMsg, ViewOpts.DemanglerOpts[0]);
     return;
   }
 
   // Parse the demangler's output.
   auto BufOrError = MemoryBuffer::getFile(OutputPath);
   if (!BufOrError) {
     error(OutputPath, BufOrError.getError().message());
     return;
   }
 
   std::unique_ptr DemanglerBuf = std::move(*BufOrError);
 
   SmallVector Symbols;
   StringRef DemanglerData = DemanglerBuf->getBuffer();
   DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols,
                       /*KeepEmpty=*/false);
   if (Symbols.size() != NumSymbols) {
     error("Demangler did not provide expected number of symbols");
     return;
   }
 
   // Cache the demangled names.
   unsigned I = 0;
   for (const auto &Function : Coverage.getCoveredFunctions())
     DemangledNames[Function.Name] = Symbols[I++];
 }
 
 StringRef CodeCoverageTool::getSymbolForHumans(StringRef Sym) const {
   const auto DemangledName = DemangledNames.find(Sym);
   if (DemangledName == DemangledNames.end())
     return Sym;
   return DemangledName->getValue();
 }
 
 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
   cl::opt ObjectFilename(
       cl::Positional, cl::Required, cl::location(this->ObjectFilename),
       cl::desc("Covered executable or object file."));
 
   cl::list InputSourceFiles(
       cl::Positional, cl::desc(""), cl::ZeroOrMore);
 
   cl::opt PGOFilename(
       "instr-profile", cl::Required, cl::location(this->PGOFilename),
       cl::desc(
           "File with the profile data obtained after an instrumented run"));
 
   cl::opt Arch(
       "arch", cl::desc("architecture of the coverage mapping binary"));
 
   cl::opt DebugDump("dump", cl::Optional,
                           cl::desc("Show internal debug dump"));
 
   cl::opt Format(
       "format", cl::desc("Output format for line-based coverage reports"),
       cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
                             "Text output"),
                  clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html",
                             "HTML output"),
                  clEnumValEnd),
       cl::init(CoverageViewOptions::OutputFormat::Text));
 
   cl::opt FilenameEquivalence(
       "filename-equivalence", cl::Optional,
       cl::desc("Treat source files as equivalent to paths in the coverage data "
                "when the file names match, even if the full paths do not"));
 
   cl::OptionCategory FilteringCategory("Function filtering options");
 
   cl::list NameFilters(
       "name", cl::Optional,
       cl::desc("Show code coverage only for functions with the given name"),
       cl::ZeroOrMore, cl::cat(FilteringCategory));
 
   cl::list NameRegexFilters(
       "name-regex", cl::Optional,
       cl::desc("Show code coverage only for functions that match the given "
                "regular expression"),
       cl::ZeroOrMore, cl::cat(FilteringCategory));
 
   cl::opt RegionCoverageLtFilter(
       "region-coverage-lt", cl::Optional,
       cl::desc("Show code coverage only for functions with region coverage "
                "less than the given threshold"),
       cl::cat(FilteringCategory));
 
   cl::opt RegionCoverageGtFilter(
       "region-coverage-gt", cl::Optional,
       cl::desc("Show code coverage only for functions with region coverage "
                "greater than the given threshold"),
       cl::cat(FilteringCategory));
 
   cl::opt LineCoverageLtFilter(
       "line-coverage-lt", cl::Optional,
       cl::desc("Show code coverage only for functions with line coverage less "
                "than the given threshold"),
       cl::cat(FilteringCategory));
 
   cl::opt LineCoverageGtFilter(
       "line-coverage-gt", cl::Optional,
       cl::desc("Show code coverage only for functions with line coverage "
                "greater than the given threshold"),
       cl::cat(FilteringCategory));
 
   cl::opt UseColor(
       "use-color", cl::desc("Emit colored output (default=autodetect)"),
       cl::init(cl::BOU_UNSET));
 
   cl::list DemanglerOpts(
       "Xdemangler", cl::desc("|"));
 
   auto commandLineParser = [&, this](int argc, const char **argv) -> int {
     cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
     ViewOpts.Debug = DebugDump;
     CompareFilenamesOnly = FilenameEquivalence;
 
     ViewOpts.Format = Format;
     switch (ViewOpts.Format) {
     case CoverageViewOptions::OutputFormat::Text:
       ViewOpts.Colors = UseColor == cl::BOU_UNSET
                             ? sys::Process::StandardOutHasColors()
                             : UseColor == cl::BOU_TRUE;
       break;
     case CoverageViewOptions::OutputFormat::HTML:
       if (UseColor == cl::BOU_FALSE)
         error("Color output cannot be disabled when generating html.");
       ViewOpts.Colors = true;
       break;
     }
 
     // If a demangler is supplied, check if it exists and register it.
     if (DemanglerOpts.size()) {
       auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]);
       if (!DemanglerPathOrErr) {
         error("Could not find the demangler!",
               DemanglerPathOrErr.getError().message());
         return 1;
       }
       DemanglerOpts[0] = *DemanglerPathOrErr;
       ViewOpts.DemanglerOpts.swap(DemanglerOpts);
     }
 
     // Create the function filters
     if (!NameFilters.empty() || !NameRegexFilters.empty()) {
       auto NameFilterer = new CoverageFilters;
       for (const auto &Name : NameFilters)
         NameFilterer->push_back(llvm::make_unique(Name));
       for (const auto &Regex : NameRegexFilters)
         NameFilterer->push_back(
             llvm::make_unique(Regex));
       Filters.push_back(std::unique_ptr(NameFilterer));
     }
     if (RegionCoverageLtFilter.getNumOccurrences() ||
         RegionCoverageGtFilter.getNumOccurrences() ||
         LineCoverageLtFilter.getNumOccurrences() ||
         LineCoverageGtFilter.getNumOccurrences()) {
       auto StatFilterer = new CoverageFilters;
       if (RegionCoverageLtFilter.getNumOccurrences())
         StatFilterer->push_back(llvm::make_unique(
             RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
       if (RegionCoverageGtFilter.getNumOccurrences())
         StatFilterer->push_back(llvm::make_unique(
             RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
       if (LineCoverageLtFilter.getNumOccurrences())
         StatFilterer->push_back(llvm::make_unique(
             LineCoverageFilter::LessThan, LineCoverageLtFilter));
       if (LineCoverageGtFilter.getNumOccurrences())
         StatFilterer->push_back(llvm::make_unique(
             RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
       Filters.push_back(std::unique_ptr(StatFilterer));
     }
 
     if (!Arch.empty() &&
         Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
       error("Unknown architecture: " + Arch);
       return 1;
     }
     CoverageArch = Arch;
 
     for (const auto &File : InputSourceFiles) {
       SmallString<128> Path(File);
       if (!CompareFilenamesOnly) {
         if (std::error_code EC = sys::fs::make_absolute(Path)) {
           error(EC.message(), File);
           return 1;
         }
       }
       addCollectedPath(Path.str());
     }
     return 0;
   };
 
   switch (Cmd) {
   case Show:
     return show(argc, argv, commandLineParser);
   case Report:
     return report(argc, argv, commandLineParser);
+  case Export:
+    return export_(argc, argv, commandLineParser);
   }
   return 0;
 }
 
 int CodeCoverageTool::show(int argc, const char **argv,
                            CommandLineParserType commandLineParser) {
 
   cl::OptionCategory ViewCategory("Viewing options");
 
   cl::opt ShowLineExecutionCounts(
       "show-line-counts", cl::Optional,
       cl::desc("Show the execution counts for each line"), cl::init(true),
       cl::cat(ViewCategory));
 
   cl::opt ShowRegions(
       "show-regions", cl::Optional,
       cl::desc("Show the execution counts for each region"),
       cl::cat(ViewCategory));
 
   cl::opt ShowBestLineRegionsCounts(
       "show-line-counts-or-regions", cl::Optional,
       cl::desc("Show the execution counts for each line, or the execution "
                "counts for each region on lines that have multiple regions"),
       cl::cat(ViewCategory));
 
   cl::opt ShowExpansions("show-expansions", cl::Optional,
                                cl::desc("Show expanded source regions"),
                                cl::cat(ViewCategory));
 
   cl::opt ShowInstantiations("show-instantiations", cl::Optional,
                                    cl::desc("Show function instantiations"),
                                    cl::cat(ViewCategory));
 
   cl::opt ShowOutputDirectory(
       "output-dir", cl::init(""),
       cl::desc("Directory in which coverage information is written out"));
   cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"),
                                  cl::aliasopt(ShowOutputDirectory));
 
   auto Err = commandLineParser(argc, argv);
   if (Err)
     return Err;
 
   ViewOpts.ShowLineNumbers = true;
   ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
                            !ShowRegions || ShowBestLineRegionsCounts;
   ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
   ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;
   ViewOpts.ShowExpandedRegions = ShowExpansions;
   ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
   ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
 
   if (ViewOpts.hasOutputDirectory()) {
     if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) {
       error("Could not create output directory!", E.message());
       return 1;
     }
   }
 
   auto Coverage = load();
   if (!Coverage)
     return 1;
 
   auto Printer = CoveragePrinter::create(ViewOpts);
 
   if (!Filters.empty()) {
     auto OSOrErr = Printer->createViewFile("functions", /*InToplevel=*/true);
     if (Error E = OSOrErr.takeError()) {
       error("Could not create view file!", toString(std::move(E)));
       return 1;
     }
     auto OS = std::move(OSOrErr.get());
 
     // Show functions.
     for (const auto &Function : Coverage->getCoveredFunctions()) {
       if (!Filters.matches(Function))
         continue;
 
       auto mainView = createFunctionView(Function, *Coverage);
       if (!mainView) {
         warning("Could not read coverage for '" + Function.Name + "'.");
         continue;
       }
 
       mainView->print(*OS.get(), /*WholeFile=*/false, /*ShowSourceName=*/true);
     }
 
     Printer->closeViewFile(std::move(OS));
     return 0;
   }
 
   // Show files
   bool ShowFilenames = SourceFiles.size() != 1;
 
   if (SourceFiles.empty())
     // Get the source files from the function coverage mapping.
     for (StringRef Filename : Coverage->getUniqueSourceFiles())
       SourceFiles.push_back(Filename);
 
   // Create an index out of the source files.
   if (ViewOpts.hasOutputDirectory()) {
     if (Error E = Printer->createIndexFile(SourceFiles)) {
       error("Could not create index file!", toString(std::move(E)));
       return 1;
     }
   }
 
   // In -output-dir mode, it's safe to use multiple threads to print files.
   unsigned ThreadCount = 1;
   if (ViewOpts.hasOutputDirectory())
     ThreadCount = std::thread::hardware_concurrency();
   ThreadPool Pool(ThreadCount);
 
   for (StringRef SourceFile : SourceFiles) {
     Pool.async([this, SourceFile, &Coverage, &Printer, ShowFilenames] {
       auto View = createSourceFileView(SourceFile, *Coverage);
       if (!View) {
         warning("The file '" + SourceFile.str() + "' isn't covered.");
         return;
       }
 
       auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false);
       if (Error E = OSOrErr.takeError()) {
         error("Could not create view file!", toString(std::move(E)));
         return;
       }
       auto OS = std::move(OSOrErr.get());
 
       View->print(*OS.get(), /*Wholefile=*/true,
                   /*ShowSourceName=*/ShowFilenames);
       Printer->closeViewFile(std::move(OS));
     });
   }
 
   Pool.wait();
 
   return 0;
 }
 
 int CodeCoverageTool::report(int argc, const char **argv,
                              CommandLineParserType commandLineParser) {
   auto Err = commandLineParser(argc, argv);
   if (Err)
     return Err;
 
   if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML)
     error("HTML output for summary reports is not yet supported.");
 
   auto Coverage = load();
   if (!Coverage)
     return 1;
 
   CoverageReport Report(ViewOpts, std::move(Coverage));
   if (SourceFiles.empty())
     Report.renderFileReports(llvm::outs());
   else
     Report.renderFunctionReports(SourceFiles, llvm::outs());
   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);
 }
 
 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 000000000000..4ce07b59f0f2
--- /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 ba60cd91da90..158415870250 100644
--- a/llvm/tools/llvm-cov/llvm-cov.cpp
+++ b/llvm/tools/llvm-cov/llvm-cov.cpp
@@ -1,94 +1,100 @@
 //===- llvm-cov.cpp - LLVM coverage tool ----------------------------------===//
 //
 //                     The LLVM Compiler Infrastructure
 //
 // This file is distributed under the University of Illinois Open Source
 // License. See LICENSE.TXT for details.
 //
 //===----------------------------------------------------------------------===//
 //
 // llvm-cov is a command line tools to analyze and report coverage information.
 //
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/Process.h"
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/raw_ostream.h"
 #include 
 
 using namespace llvm;
 
 /// \brief The main entry point for the 'show' subcommand.
 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[]);
 
 /// \brief The main entry point for the gcov compatible coverage tool.
 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;
 }
 
 /// \brief Top level version information.
 static int versionMain(int argc, const char *argv[]) {
   cl::PrintVersionMessage();
   return 0;
 }
 
 int main(int argc, const char **argv) {
   // Print a stack trace if we signal out.
   sys::PrintStackTraceOnErrorSignal(argv[0]);
   PrettyStackTraceProgram X(argc, argv);
   llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
 
   // If argv[0] is or ends with 'gcov', always be gcov compatible
   if (sys::path::stem(argv[0]).endswith_lower("gcov"))
     return gcovMain(argc, argv);
 
   // Check if we are invoking a specific tool command.
   if (argc > 1) {
     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)
                             .Cases("-h", "-help", "--help", helpMain)
                             .Cases("-version", "--version", versionMain)
                             .Default(nullptr);
 
     if (Func) {
       std::string Invocation = std::string(argv[0]) + " " + argv[1];
       argv[1] = Invocation.c_str();
       return Func(argc - 1, argv + 1);
     }
   }
 
   if (argc > 1) {
     if (sys::Process::StandardErrHasColors())
       errs().changeColor(raw_ostream::RED);
     errs() << "Unrecognized command: " << argv[1] << ".\n\n";
     if (sys::Process::StandardErrHasColors())
       errs().resetColor();
   }
   helpMain(argc, argv);
   return 1;
 }