xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-cov/CoverageExporterJson.cpp (revision e8d8bef961a50d4dc22501cde4fb9fb0be1b2532)
10b57cec5SDimitry Andric //===- CoverageExporterJson.cpp - Code coverage export --------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file implements export of code coverage data to JSON.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
140b57cec5SDimitry Andric //
150b57cec5SDimitry Andric // The json code coverage export follows the following format
160b57cec5SDimitry Andric // Root: dict => Root Element containing metadata
170b57cec5SDimitry Andric // -- Data: array => Homogeneous array of one or more export objects
180b57cec5SDimitry Andric //   -- Export: dict => Json representation of one CoverageMapping
190b57cec5SDimitry Andric //     -- Files: array => List of objects describing coverage for files
200b57cec5SDimitry Andric //       -- File: dict => Coverage for a single file
21*e8d8bef9SDimitry Andric //         -- Branches: array => List of Branches in the file
22*e8d8bef9SDimitry Andric //           -- Branch: dict => Describes a branch of the file with counters
230b57cec5SDimitry Andric //         -- Segments: array => List of Segments contained in the file
240b57cec5SDimitry Andric //           -- Segment: dict => Describes a segment of the file with a counter
250b57cec5SDimitry Andric //         -- Expansions: array => List of expansion records
260b57cec5SDimitry Andric //           -- Expansion: dict => Object that descibes a single expansion
270b57cec5SDimitry Andric //             -- CountedRegion: dict => The region to be expanded
280b57cec5SDimitry Andric //             -- TargetRegions: array => List of Regions in the expansion
290b57cec5SDimitry Andric //               -- CountedRegion: dict => Single Region in the expansion
30*e8d8bef9SDimitry Andric //             -- Branches: array => List of Branches in the expansion
31*e8d8bef9SDimitry Andric //               -- Branch: dict => Describes a branch in expansion and counters
320b57cec5SDimitry Andric //         -- Summary: dict => Object summarizing the coverage for this file
330b57cec5SDimitry Andric //           -- LineCoverage: dict => Object summarizing line coverage
340b57cec5SDimitry Andric //           -- FunctionCoverage: dict => Object summarizing function coverage
350b57cec5SDimitry Andric //           -- RegionCoverage: dict => Object summarizing region coverage
36*e8d8bef9SDimitry Andric //           -- BranchCoverage: dict => Object summarizing branch coverage
370b57cec5SDimitry Andric //     -- Functions: array => List of objects describing coverage for functions
380b57cec5SDimitry Andric //       -- Function: dict => Coverage info for a single function
390b57cec5SDimitry Andric //         -- Filenames: array => List of filenames that the function relates to
400b57cec5SDimitry Andric //   -- Summary: dict => Object summarizing the coverage for the entire binary
410b57cec5SDimitry Andric //     -- LineCoverage: dict => Object summarizing line coverage
420b57cec5SDimitry Andric //     -- FunctionCoverage: dict => Object summarizing function coverage
430b57cec5SDimitry Andric //     -- InstantiationCoverage: dict => Object summarizing inst. coverage
440b57cec5SDimitry Andric //     -- RegionCoverage: dict => Object summarizing region coverage
45*e8d8bef9SDimitry Andric //     -- BranchCoverage: dict => Object summarizing branch coverage
460b57cec5SDimitry Andric //
470b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
480b57cec5SDimitry Andric 
490b57cec5SDimitry Andric #include "CoverageExporterJson.h"
500b57cec5SDimitry Andric #include "CoverageReport.h"
510b57cec5SDimitry Andric #include "llvm/ADT/Optional.h"
520b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
530b57cec5SDimitry Andric #include "llvm/Support/JSON.h"
540b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h"
550b57cec5SDimitry Andric #include "llvm/Support/Threading.h"
560b57cec5SDimitry Andric #include <algorithm>
57480093f4SDimitry Andric #include <limits>
580b57cec5SDimitry Andric #include <mutex>
590b57cec5SDimitry Andric #include <utility>
600b57cec5SDimitry Andric 
610b57cec5SDimitry Andric /// The semantic version combined as a string.
625ffd83dbSDimitry Andric #define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.1"
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric /// Unique type identifier for JSON coverage export.
650b57cec5SDimitry Andric #define LLVM_COVERAGE_EXPORT_JSON_TYPE_STR "llvm.coverage.json.export"
660b57cec5SDimitry Andric 
670b57cec5SDimitry Andric using namespace llvm;
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric namespace {
700b57cec5SDimitry Andric 
71480093f4SDimitry Andric // The JSON library accepts int64_t, but profiling counts are stored as uint64_t.
72480093f4SDimitry Andric // Therefore we need to explicitly convert from unsigned to signed, since a naive
73480093f4SDimitry Andric // cast is implementation-defined behavior when the unsigned value cannot be
74480093f4SDimitry Andric // represented as a signed value. We choose to clamp the values to preserve the
75480093f4SDimitry Andric // invariant that counts are always >= 0.
76480093f4SDimitry Andric int64_t clamp_uint64_to_int64(uint64_t u) {
77480093f4SDimitry Andric   return std::min(u, static_cast<uint64_t>(std::numeric_limits<int64_t>::max()));
78480093f4SDimitry Andric }
79480093f4SDimitry Andric 
800b57cec5SDimitry Andric json::Array renderSegment(const coverage::CoverageSegment &Segment) {
815ffd83dbSDimitry Andric   return json::Array({Segment.Line, Segment.Col,
825ffd83dbSDimitry Andric                       clamp_uint64_to_int64(Segment.Count), Segment.HasCount,
835ffd83dbSDimitry Andric                       Segment.IsRegionEntry, Segment.IsGapRegion});
840b57cec5SDimitry Andric }
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric json::Array renderRegion(const coverage::CountedRegion &Region) {
870b57cec5SDimitry Andric   return json::Array({Region.LineStart, Region.ColumnStart, Region.LineEnd,
88480093f4SDimitry Andric                       Region.ColumnEnd, clamp_uint64_to_int64(Region.ExecutionCount),
890b57cec5SDimitry Andric                       Region.FileID, Region.ExpandedFileID,
900b57cec5SDimitry Andric                       int64_t(Region.Kind)});
910b57cec5SDimitry Andric }
920b57cec5SDimitry Andric 
93*e8d8bef9SDimitry Andric json::Array renderBranch(const coverage::CountedRegion &Region) {
94*e8d8bef9SDimitry Andric   return json::Array(
95*e8d8bef9SDimitry Andric       {Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd,
96*e8d8bef9SDimitry Andric        clamp_uint64_to_int64(Region.ExecutionCount),
97*e8d8bef9SDimitry Andric        clamp_uint64_to_int64(Region.FalseExecutionCount), Region.FileID,
98*e8d8bef9SDimitry Andric        Region.ExpandedFileID, int64_t(Region.Kind)});
99*e8d8bef9SDimitry Andric }
100*e8d8bef9SDimitry Andric 
1010b57cec5SDimitry Andric json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) {
1020b57cec5SDimitry Andric   json::Array RegionArray;
1030b57cec5SDimitry Andric   for (const auto &Region : Regions)
1040b57cec5SDimitry Andric     RegionArray.push_back(renderRegion(Region));
1050b57cec5SDimitry Andric   return RegionArray;
1060b57cec5SDimitry Andric }
1070b57cec5SDimitry Andric 
108*e8d8bef9SDimitry Andric json::Array renderBranchRegions(ArrayRef<coverage::CountedRegion> Regions) {
109*e8d8bef9SDimitry Andric   json::Array RegionArray;
110*e8d8bef9SDimitry Andric   for (const auto &Region : Regions)
111*e8d8bef9SDimitry Andric     if (!Region.Folded)
112*e8d8bef9SDimitry Andric       RegionArray.push_back(renderBranch(Region));
113*e8d8bef9SDimitry Andric   return RegionArray;
114*e8d8bef9SDimitry Andric }
115*e8d8bef9SDimitry Andric 
116*e8d8bef9SDimitry Andric std::vector<llvm::coverage::CountedRegion>
117*e8d8bef9SDimitry Andric collectNestedBranches(const coverage::CoverageMapping &Coverage,
118*e8d8bef9SDimitry Andric                       ArrayRef<llvm::coverage::ExpansionRecord> Expansions) {
119*e8d8bef9SDimitry Andric   std::vector<llvm::coverage::CountedRegion> Branches;
120*e8d8bef9SDimitry Andric   for (const auto &Expansion : Expansions) {
121*e8d8bef9SDimitry Andric     auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
122*e8d8bef9SDimitry Andric 
123*e8d8bef9SDimitry Andric     // Recursively collect branches from nested expansions.
124*e8d8bef9SDimitry Andric     auto NestedExpansions = ExpansionCoverage.getExpansions();
125*e8d8bef9SDimitry Andric     auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions);
126*e8d8bef9SDimitry Andric     Branches.insert(Branches.end(), NestedExBranches.begin(),
127*e8d8bef9SDimitry Andric                     NestedExBranches.end());
128*e8d8bef9SDimitry Andric 
129*e8d8bef9SDimitry Andric     // Add branches from this level of expansion.
130*e8d8bef9SDimitry Andric     auto ExBranches = ExpansionCoverage.getBranches();
131*e8d8bef9SDimitry Andric     for (auto B : ExBranches)
132*e8d8bef9SDimitry Andric       if (B.FileID == Expansion.FileID)
133*e8d8bef9SDimitry Andric         Branches.push_back(B);
134*e8d8bef9SDimitry Andric   }
135*e8d8bef9SDimitry Andric 
136*e8d8bef9SDimitry Andric   return Branches;
137*e8d8bef9SDimitry Andric }
138*e8d8bef9SDimitry Andric 
139*e8d8bef9SDimitry Andric json::Object renderExpansion(const coverage::CoverageMapping &Coverage,
140*e8d8bef9SDimitry Andric                              const coverage::ExpansionRecord &Expansion) {
141*e8d8bef9SDimitry Andric   std::vector<llvm::coverage::ExpansionRecord> Expansions = {Expansion};
1420b57cec5SDimitry Andric   return json::Object(
1430b57cec5SDimitry Andric       {{"filenames", json::Array(Expansion.Function.Filenames)},
1440b57cec5SDimitry Andric        // Mark the beginning and end of this expansion in the source file.
1450b57cec5SDimitry Andric        {"source_region", renderRegion(Expansion.Region)},
1460b57cec5SDimitry Andric        // Enumerate the coverage information for the expansion.
147*e8d8bef9SDimitry Andric        {"target_regions", renderRegions(Expansion.Function.CountedRegions)},
148*e8d8bef9SDimitry Andric        // Enumerate the branch coverage information for the expansion.
149*e8d8bef9SDimitry Andric        {"branches",
150*e8d8bef9SDimitry Andric         renderBranchRegions(collectNestedBranches(Coverage, Expansions))}});
1510b57cec5SDimitry Andric }
1520b57cec5SDimitry Andric 
1530b57cec5SDimitry Andric json::Object renderSummary(const FileCoverageSummary &Summary) {
1540b57cec5SDimitry Andric   return json::Object(
1550b57cec5SDimitry Andric       {{"lines",
1560b57cec5SDimitry Andric         json::Object({{"count", int64_t(Summary.LineCoverage.getNumLines())},
1570b57cec5SDimitry Andric                       {"covered", int64_t(Summary.LineCoverage.getCovered())},
1580b57cec5SDimitry Andric                       {"percent", Summary.LineCoverage.getPercentCovered()}})},
1590b57cec5SDimitry Andric        {"functions",
1600b57cec5SDimitry Andric         json::Object(
1610b57cec5SDimitry Andric             {{"count", int64_t(Summary.FunctionCoverage.getNumFunctions())},
1620b57cec5SDimitry Andric              {"covered", int64_t(Summary.FunctionCoverage.getExecuted())},
1630b57cec5SDimitry Andric              {"percent", Summary.FunctionCoverage.getPercentCovered()}})},
1640b57cec5SDimitry Andric        {"instantiations",
1650b57cec5SDimitry Andric         json::Object(
1660b57cec5SDimitry Andric             {{"count",
1670b57cec5SDimitry Andric               int64_t(Summary.InstantiationCoverage.getNumFunctions())},
1680b57cec5SDimitry Andric              {"covered", int64_t(Summary.InstantiationCoverage.getExecuted())},
1690b57cec5SDimitry Andric              {"percent", Summary.InstantiationCoverage.getPercentCovered()}})},
1700b57cec5SDimitry Andric        {"regions",
1710b57cec5SDimitry Andric         json::Object(
1720b57cec5SDimitry Andric             {{"count", int64_t(Summary.RegionCoverage.getNumRegions())},
1730b57cec5SDimitry Andric              {"covered", int64_t(Summary.RegionCoverage.getCovered())},
1740b57cec5SDimitry Andric              {"notcovered", int64_t(Summary.RegionCoverage.getNumRegions() -
1750b57cec5SDimitry Andric                                     Summary.RegionCoverage.getCovered())},
176*e8d8bef9SDimitry Andric              {"percent", Summary.RegionCoverage.getPercentCovered()}})},
177*e8d8bef9SDimitry Andric        {"branches",
178*e8d8bef9SDimitry Andric         json::Object(
179*e8d8bef9SDimitry Andric             {{"count", int64_t(Summary.BranchCoverage.getNumBranches())},
180*e8d8bef9SDimitry Andric              {"covered", int64_t(Summary.BranchCoverage.getCovered())},
181*e8d8bef9SDimitry Andric              {"notcovered", int64_t(Summary.BranchCoverage.getNumBranches() -
182*e8d8bef9SDimitry Andric                                     Summary.BranchCoverage.getCovered())},
183*e8d8bef9SDimitry Andric              {"percent", Summary.BranchCoverage.getPercentCovered()}})}});
1840b57cec5SDimitry Andric }
1850b57cec5SDimitry Andric 
186*e8d8bef9SDimitry Andric json::Array renderFileExpansions(const coverage::CoverageMapping &Coverage,
187*e8d8bef9SDimitry Andric                                  const coverage::CoverageData &FileCoverage,
1880b57cec5SDimitry Andric                                  const FileCoverageSummary &FileReport) {
1890b57cec5SDimitry Andric   json::Array ExpansionArray;
1900b57cec5SDimitry Andric   for (const auto &Expansion : FileCoverage.getExpansions())
191*e8d8bef9SDimitry Andric     ExpansionArray.push_back(renderExpansion(Coverage, Expansion));
1920b57cec5SDimitry Andric   return ExpansionArray;
1930b57cec5SDimitry Andric }
1940b57cec5SDimitry Andric 
1950b57cec5SDimitry Andric json::Array renderFileSegments(const coverage::CoverageData &FileCoverage,
1960b57cec5SDimitry Andric                                const FileCoverageSummary &FileReport) {
1970b57cec5SDimitry Andric   json::Array SegmentArray;
1980b57cec5SDimitry Andric   for (const auto &Segment : FileCoverage)
1990b57cec5SDimitry Andric     SegmentArray.push_back(renderSegment(Segment));
2000b57cec5SDimitry Andric   return SegmentArray;
2010b57cec5SDimitry Andric }
2020b57cec5SDimitry Andric 
203*e8d8bef9SDimitry Andric json::Array renderFileBranches(const coverage::CoverageData &FileCoverage,
204*e8d8bef9SDimitry Andric                                const FileCoverageSummary &FileReport) {
205*e8d8bef9SDimitry Andric   json::Array BranchArray;
206*e8d8bef9SDimitry Andric   for (const auto &Branch : FileCoverage.getBranches())
207*e8d8bef9SDimitry Andric     BranchArray.push_back(renderBranch(Branch));
208*e8d8bef9SDimitry Andric   return BranchArray;
209*e8d8bef9SDimitry Andric }
210*e8d8bef9SDimitry Andric 
2110b57cec5SDimitry Andric json::Object renderFile(const coverage::CoverageMapping &Coverage,
2120b57cec5SDimitry Andric                         const std::string &Filename,
2130b57cec5SDimitry Andric                         const FileCoverageSummary &FileReport,
2140b57cec5SDimitry Andric                         const CoverageViewOptions &Options) {
2150b57cec5SDimitry Andric   json::Object File({{"filename", Filename}});
2160b57cec5SDimitry Andric   if (!Options.ExportSummaryOnly) {
2170b57cec5SDimitry Andric     // Calculate and render detailed coverage information for given file.
2180b57cec5SDimitry Andric     auto FileCoverage = Coverage.getCoverageForFile(Filename);
2190b57cec5SDimitry Andric     File["segments"] = renderFileSegments(FileCoverage, FileReport);
220*e8d8bef9SDimitry Andric     File["branches"] = renderFileBranches(FileCoverage, FileReport);
2210b57cec5SDimitry Andric     if (!Options.SkipExpansions) {
222*e8d8bef9SDimitry Andric       File["expansions"] =
223*e8d8bef9SDimitry Andric           renderFileExpansions(Coverage, FileCoverage, FileReport);
2240b57cec5SDimitry Andric     }
2250b57cec5SDimitry Andric   }
2260b57cec5SDimitry Andric   File["summary"] = renderSummary(FileReport);
2270b57cec5SDimitry Andric   return File;
2280b57cec5SDimitry Andric }
2290b57cec5SDimitry Andric 
2300b57cec5SDimitry Andric json::Array renderFiles(const coverage::CoverageMapping &Coverage,
2310b57cec5SDimitry Andric                         ArrayRef<std::string> SourceFiles,
2320b57cec5SDimitry Andric                         ArrayRef<FileCoverageSummary> FileReports,
2330b57cec5SDimitry Andric                         const CoverageViewOptions &Options) {
2345ffd83dbSDimitry Andric   ThreadPoolStrategy S = hardware_concurrency(Options.NumThreads);
2355ffd83dbSDimitry Andric   if (Options.NumThreads == 0) {
2365ffd83dbSDimitry Andric     // If NumThreads is not specified, create one thread for each input, up to
2375ffd83dbSDimitry Andric     // the number of hardware cores.
2385ffd83dbSDimitry Andric     S = heavyweight_hardware_concurrency(SourceFiles.size());
2395ffd83dbSDimitry Andric     S.Limit = true;
2400b57cec5SDimitry Andric   }
2415ffd83dbSDimitry Andric   ThreadPool Pool(S);
2420b57cec5SDimitry Andric   json::Array FileArray;
2430b57cec5SDimitry Andric   std::mutex FileArrayMutex;
2440b57cec5SDimitry Andric 
2450b57cec5SDimitry Andric   for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) {
2460b57cec5SDimitry Andric     auto &SourceFile = SourceFiles[I];
2470b57cec5SDimitry Andric     auto &FileReport = FileReports[I];
2480b57cec5SDimitry Andric     Pool.async([&] {
2490b57cec5SDimitry Andric       auto File = renderFile(Coverage, SourceFile, FileReport, Options);
2500b57cec5SDimitry Andric       {
2510b57cec5SDimitry Andric         std::lock_guard<std::mutex> Lock(FileArrayMutex);
2520b57cec5SDimitry Andric         FileArray.push_back(std::move(File));
2530b57cec5SDimitry Andric       }
2540b57cec5SDimitry Andric     });
2550b57cec5SDimitry Andric   }
2560b57cec5SDimitry Andric   Pool.wait();
2570b57cec5SDimitry Andric   return FileArray;
2580b57cec5SDimitry Andric }
2590b57cec5SDimitry Andric 
2600b57cec5SDimitry Andric json::Array renderFunctions(
2610b57cec5SDimitry Andric     const iterator_range<coverage::FunctionRecordIterator> &Functions) {
2620b57cec5SDimitry Andric   json::Array FunctionArray;
2630b57cec5SDimitry Andric   for (const auto &F : Functions)
2640b57cec5SDimitry Andric     FunctionArray.push_back(
2650b57cec5SDimitry Andric         json::Object({{"name", F.Name},
266480093f4SDimitry Andric                       {"count", clamp_uint64_to_int64(F.ExecutionCount)},
2670b57cec5SDimitry Andric                       {"regions", renderRegions(F.CountedRegions)},
268*e8d8bef9SDimitry Andric                       {"branches", renderBranchRegions(F.CountedBranchRegions)},
2690b57cec5SDimitry Andric                       {"filenames", json::Array(F.Filenames)}}));
2700b57cec5SDimitry Andric   return FunctionArray;
2710b57cec5SDimitry Andric }
2720b57cec5SDimitry Andric 
2730b57cec5SDimitry Andric } // end anonymous namespace
2740b57cec5SDimitry Andric 
2750b57cec5SDimitry Andric void CoverageExporterJson::renderRoot(const CoverageFilters &IgnoreFilters) {
2760b57cec5SDimitry Andric   std::vector<std::string> SourceFiles;
2770b57cec5SDimitry Andric   for (StringRef SF : Coverage.getUniqueSourceFiles()) {
2780b57cec5SDimitry Andric     if (!IgnoreFilters.matchesFilename(SF))
2790b57cec5SDimitry Andric       SourceFiles.emplace_back(SF);
2800b57cec5SDimitry Andric   }
2810b57cec5SDimitry Andric   renderRoot(SourceFiles);
2820b57cec5SDimitry Andric }
2830b57cec5SDimitry Andric 
2840b57cec5SDimitry Andric void CoverageExporterJson::renderRoot(ArrayRef<std::string> SourceFiles) {
2850b57cec5SDimitry Andric   FileCoverageSummary Totals = FileCoverageSummary("Totals");
2860b57cec5SDimitry Andric   auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
2870b57cec5SDimitry Andric                                                         SourceFiles, Options);
2880b57cec5SDimitry Andric   auto Files = renderFiles(Coverage, SourceFiles, FileReports, Options);
2890b57cec5SDimitry Andric   // Sort files in order of their names.
290*e8d8bef9SDimitry Andric   llvm::sort(Files, [](const json::Value &A, const json::Value &B) {
2910b57cec5SDimitry Andric     const json::Object *ObjA = A.getAsObject();
2920b57cec5SDimitry Andric     const json::Object *ObjB = B.getAsObject();
2930b57cec5SDimitry Andric     assert(ObjA != nullptr && "Value A was not an Object");
2940b57cec5SDimitry Andric     assert(ObjB != nullptr && "Value B was not an Object");
2950b57cec5SDimitry Andric     const StringRef FilenameA = ObjA->getString("filename").getValue();
2960b57cec5SDimitry Andric     const StringRef FilenameB = ObjB->getString("filename").getValue();
2970b57cec5SDimitry Andric     return FilenameA.compare(FilenameB) < 0;
2980b57cec5SDimitry Andric   });
2990b57cec5SDimitry Andric   auto Export = json::Object(
3000b57cec5SDimitry Andric       {{"files", std::move(Files)}, {"totals", renderSummary(Totals)}});
3010b57cec5SDimitry Andric   // Skip functions-level information  if necessary.
3020b57cec5SDimitry Andric   if (!Options.ExportSummaryOnly && !Options.SkipFunctions)
3030b57cec5SDimitry Andric     Export["functions"] = renderFunctions(Coverage.getCoveredFunctions());
3040b57cec5SDimitry Andric 
3050b57cec5SDimitry Andric   auto ExportArray = json::Array({std::move(Export)});
3060b57cec5SDimitry Andric 
3070b57cec5SDimitry Andric   OS << json::Object({{"version", LLVM_COVERAGE_EXPORT_JSON_STR},
3080b57cec5SDimitry Andric                       {"type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR},
3090b57cec5SDimitry Andric                       {"data", std::move(ExportArray)}});
3100b57cec5SDimitry Andric }
311