xref: /freebsd-src/contrib/llvm-project/llvm/tools/llvm-cov/CoverageExporterJson.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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
21e8d8bef9SDimitry Andric //         -- Branches: array => List of Branches in the file
22e8d8bef9SDimitry Andric //           -- Branch: dict => Describes a branch of the file with counters
235f757f3fSDimitry Andric //         -- MCDC Records: array => List of MCDC records in the file
245f757f3fSDimitry Andric //           -- MCDC Values: array => List of T/F covered condition values
250b57cec5SDimitry Andric //         -- Segments: array => List of Segments contained in the file
260b57cec5SDimitry Andric //           -- Segment: dict => Describes a segment of the file with a counter
270b57cec5SDimitry Andric //         -- Expansions: array => List of expansion records
280b57cec5SDimitry Andric //           -- Expansion: dict => Object that descibes a single expansion
290b57cec5SDimitry Andric //             -- CountedRegion: dict => The region to be expanded
300b57cec5SDimitry Andric //             -- TargetRegions: array => List of Regions in the expansion
310b57cec5SDimitry Andric //               -- CountedRegion: dict => Single Region in the expansion
32e8d8bef9SDimitry Andric //             -- Branches: array => List of Branches in the expansion
33e8d8bef9SDimitry Andric //               -- Branch: dict => Describes a branch in expansion and counters
340b57cec5SDimitry Andric //         -- Summary: dict => Object summarizing the coverage for this file
350b57cec5SDimitry Andric //           -- LineCoverage: dict => Object summarizing line coverage
360b57cec5SDimitry Andric //           -- FunctionCoverage: dict => Object summarizing function coverage
370b57cec5SDimitry Andric //           -- RegionCoverage: dict => Object summarizing region coverage
38e8d8bef9SDimitry Andric //           -- BranchCoverage: dict => Object summarizing branch coverage
395f757f3fSDimitry Andric //           -- MCDCCoverage: dict => Object summarizing MC/DC coverage
400b57cec5SDimitry Andric //     -- Functions: array => List of objects describing coverage for functions
410b57cec5SDimitry Andric //       -- Function: dict => Coverage info for a single function
420b57cec5SDimitry Andric //         -- Filenames: array => List of filenames that the function relates to
430b57cec5SDimitry Andric //   -- Summary: dict => Object summarizing the coverage for the entire binary
440b57cec5SDimitry Andric //     -- LineCoverage: dict => Object summarizing line coverage
450b57cec5SDimitry Andric //     -- FunctionCoverage: dict => Object summarizing function coverage
460b57cec5SDimitry Andric //     -- InstantiationCoverage: dict => Object summarizing inst. coverage
470b57cec5SDimitry Andric //     -- RegionCoverage: dict => Object summarizing region coverage
48e8d8bef9SDimitry Andric //     -- BranchCoverage: dict => Object summarizing branch coverage
495f757f3fSDimitry Andric //     -- MCDCCoverage: dict => Object summarizing MC/DC coverage
500b57cec5SDimitry Andric //
510b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric #include "CoverageExporterJson.h"
540b57cec5SDimitry Andric #include "CoverageReport.h"
550b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
560b57cec5SDimitry Andric #include "llvm/Support/JSON.h"
570b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h"
580b57cec5SDimitry Andric #include "llvm/Support/Threading.h"
590b57cec5SDimitry Andric #include <algorithm>
60480093f4SDimitry Andric #include <limits>
610b57cec5SDimitry Andric #include <mutex>
620b57cec5SDimitry Andric #include <utility>
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric /// The semantic version combined as a string.
655ffd83dbSDimitry Andric #define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.1"
660b57cec5SDimitry Andric 
670b57cec5SDimitry Andric /// Unique type identifier for JSON coverage export.
680b57cec5SDimitry Andric #define LLVM_COVERAGE_EXPORT_JSON_TYPE_STR "llvm.coverage.json.export"
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric using namespace llvm;
710b57cec5SDimitry Andric 
720b57cec5SDimitry Andric namespace {
730b57cec5SDimitry Andric 
74480093f4SDimitry Andric // The JSON library accepts int64_t, but profiling counts are stored as uint64_t.
75480093f4SDimitry Andric // Therefore we need to explicitly convert from unsigned to signed, since a naive
76480093f4SDimitry Andric // cast is implementation-defined behavior when the unsigned value cannot be
77480093f4SDimitry Andric // represented as a signed value. We choose to clamp the values to preserve the
78480093f4SDimitry Andric // invariant that counts are always >= 0.
79480093f4SDimitry Andric int64_t clamp_uint64_to_int64(uint64_t u) {
80480093f4SDimitry Andric   return std::min(u, static_cast<uint64_t>(std::numeric_limits<int64_t>::max()));
81480093f4SDimitry Andric }
82480093f4SDimitry Andric 
830b57cec5SDimitry Andric json::Array renderSegment(const coverage::CoverageSegment &Segment) {
845ffd83dbSDimitry Andric   return json::Array({Segment.Line, Segment.Col,
855ffd83dbSDimitry Andric                       clamp_uint64_to_int64(Segment.Count), Segment.HasCount,
865ffd83dbSDimitry Andric                       Segment.IsRegionEntry, Segment.IsGapRegion});
870b57cec5SDimitry Andric }
880b57cec5SDimitry Andric 
890b57cec5SDimitry Andric json::Array renderRegion(const coverage::CountedRegion &Region) {
900b57cec5SDimitry Andric   return json::Array({Region.LineStart, Region.ColumnStart, Region.LineEnd,
91480093f4SDimitry Andric                       Region.ColumnEnd, clamp_uint64_to_int64(Region.ExecutionCount),
920b57cec5SDimitry Andric                       Region.FileID, Region.ExpandedFileID,
930b57cec5SDimitry Andric                       int64_t(Region.Kind)});
940b57cec5SDimitry Andric }
950b57cec5SDimitry Andric 
96e8d8bef9SDimitry Andric json::Array renderBranch(const coverage::CountedRegion &Region) {
97e8d8bef9SDimitry Andric   return json::Array(
98e8d8bef9SDimitry Andric       {Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd,
99e8d8bef9SDimitry Andric        clamp_uint64_to_int64(Region.ExecutionCount),
100e8d8bef9SDimitry Andric        clamp_uint64_to_int64(Region.FalseExecutionCount), Region.FileID,
101e8d8bef9SDimitry Andric        Region.ExpandedFileID, int64_t(Region.Kind)});
102e8d8bef9SDimitry Andric }
103e8d8bef9SDimitry Andric 
1045f757f3fSDimitry Andric json::Array gatherConditions(const coverage::MCDCRecord &Record) {
1055f757f3fSDimitry Andric   json::Array Conditions;
1065f757f3fSDimitry Andric   for (unsigned c = 0; c < Record.getNumConditions(); c++)
1075f757f3fSDimitry Andric     Conditions.push_back(Record.isConditionIndependencePairCovered(c));
1085f757f3fSDimitry Andric   return Conditions;
1095f757f3fSDimitry Andric }
1105f757f3fSDimitry Andric 
1115f757f3fSDimitry Andric json::Array renderMCDCRecord(const coverage::MCDCRecord &Record) {
1125f757f3fSDimitry Andric   const llvm::coverage::CounterMappingRegion &CMR = Record.getDecisionRegion();
1135f757f3fSDimitry Andric   return json::Array({CMR.LineStart, CMR.ColumnStart, CMR.LineEnd,
1145f757f3fSDimitry Andric                       CMR.ColumnEnd, CMR.ExpandedFileID, int64_t(CMR.Kind),
1155f757f3fSDimitry Andric                       gatherConditions(Record)});
1165f757f3fSDimitry Andric }
1175f757f3fSDimitry Andric 
1180b57cec5SDimitry Andric json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) {
1190b57cec5SDimitry Andric   json::Array RegionArray;
1200b57cec5SDimitry Andric   for (const auto &Region : Regions)
1210b57cec5SDimitry Andric     RegionArray.push_back(renderRegion(Region));
1220b57cec5SDimitry Andric   return RegionArray;
1230b57cec5SDimitry Andric }
1240b57cec5SDimitry Andric 
125e8d8bef9SDimitry Andric json::Array renderBranchRegions(ArrayRef<coverage::CountedRegion> Regions) {
126e8d8bef9SDimitry Andric   json::Array RegionArray;
127e8d8bef9SDimitry Andric   for (const auto &Region : Regions)
128e8d8bef9SDimitry Andric     if (!Region.Folded)
129e8d8bef9SDimitry Andric       RegionArray.push_back(renderBranch(Region));
130e8d8bef9SDimitry Andric   return RegionArray;
131e8d8bef9SDimitry Andric }
132e8d8bef9SDimitry Andric 
1335f757f3fSDimitry Andric json::Array renderMCDCRecords(ArrayRef<coverage::MCDCRecord> Records) {
1345f757f3fSDimitry Andric   json::Array RecordArray;
1355f757f3fSDimitry Andric   for (auto &Record : Records)
1365f757f3fSDimitry Andric     RecordArray.push_back(renderMCDCRecord(Record));
1375f757f3fSDimitry Andric   return RecordArray;
1385f757f3fSDimitry Andric }
1395f757f3fSDimitry Andric 
140e8d8bef9SDimitry Andric std::vector<llvm::coverage::CountedRegion>
141e8d8bef9SDimitry Andric collectNestedBranches(const coverage::CoverageMapping &Coverage,
142e8d8bef9SDimitry Andric                       ArrayRef<llvm::coverage::ExpansionRecord> Expansions) {
143e8d8bef9SDimitry Andric   std::vector<llvm::coverage::CountedRegion> Branches;
144e8d8bef9SDimitry Andric   for (const auto &Expansion : Expansions) {
145e8d8bef9SDimitry Andric     auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
146e8d8bef9SDimitry Andric 
147e8d8bef9SDimitry Andric     // Recursively collect branches from nested expansions.
148e8d8bef9SDimitry Andric     auto NestedExpansions = ExpansionCoverage.getExpansions();
149e8d8bef9SDimitry Andric     auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions);
150fe6060f1SDimitry Andric     append_range(Branches, NestedExBranches);
151e8d8bef9SDimitry Andric 
152e8d8bef9SDimitry Andric     // Add branches from this level of expansion.
153e8d8bef9SDimitry Andric     auto ExBranches = ExpansionCoverage.getBranches();
154e8d8bef9SDimitry Andric     for (auto B : ExBranches)
155e8d8bef9SDimitry Andric       if (B.FileID == Expansion.FileID)
156e8d8bef9SDimitry Andric         Branches.push_back(B);
157e8d8bef9SDimitry Andric   }
158e8d8bef9SDimitry Andric 
159e8d8bef9SDimitry Andric   return Branches;
160e8d8bef9SDimitry Andric }
161e8d8bef9SDimitry Andric 
162e8d8bef9SDimitry Andric json::Object renderExpansion(const coverage::CoverageMapping &Coverage,
163e8d8bef9SDimitry Andric                              const coverage::ExpansionRecord &Expansion) {
164e8d8bef9SDimitry Andric   std::vector<llvm::coverage::ExpansionRecord> Expansions = {Expansion};
1650b57cec5SDimitry Andric   return json::Object(
1660b57cec5SDimitry Andric       {{"filenames", json::Array(Expansion.Function.Filenames)},
1670b57cec5SDimitry Andric        // Mark the beginning and end of this expansion in the source file.
1680b57cec5SDimitry Andric        {"source_region", renderRegion(Expansion.Region)},
1690b57cec5SDimitry Andric        // Enumerate the coverage information for the expansion.
170e8d8bef9SDimitry Andric        {"target_regions", renderRegions(Expansion.Function.CountedRegions)},
171e8d8bef9SDimitry Andric        // Enumerate the branch coverage information for the expansion.
172e8d8bef9SDimitry Andric        {"branches",
173e8d8bef9SDimitry Andric         renderBranchRegions(collectNestedBranches(Coverage, Expansions))}});
1740b57cec5SDimitry Andric }
1750b57cec5SDimitry Andric 
1760b57cec5SDimitry Andric json::Object renderSummary(const FileCoverageSummary &Summary) {
1770b57cec5SDimitry Andric   return json::Object(
1780b57cec5SDimitry Andric       {{"lines",
1790b57cec5SDimitry Andric         json::Object({{"count", int64_t(Summary.LineCoverage.getNumLines())},
1800b57cec5SDimitry Andric                       {"covered", int64_t(Summary.LineCoverage.getCovered())},
1810b57cec5SDimitry Andric                       {"percent", Summary.LineCoverage.getPercentCovered()}})},
1820b57cec5SDimitry Andric        {"functions",
1830b57cec5SDimitry Andric         json::Object(
1840b57cec5SDimitry Andric             {{"count", int64_t(Summary.FunctionCoverage.getNumFunctions())},
1850b57cec5SDimitry Andric              {"covered", int64_t(Summary.FunctionCoverage.getExecuted())},
1860b57cec5SDimitry Andric              {"percent", Summary.FunctionCoverage.getPercentCovered()}})},
1870b57cec5SDimitry Andric        {"instantiations",
1880b57cec5SDimitry Andric         json::Object(
1890b57cec5SDimitry Andric             {{"count",
1900b57cec5SDimitry Andric               int64_t(Summary.InstantiationCoverage.getNumFunctions())},
1910b57cec5SDimitry Andric              {"covered", int64_t(Summary.InstantiationCoverage.getExecuted())},
1920b57cec5SDimitry Andric              {"percent", Summary.InstantiationCoverage.getPercentCovered()}})},
1930b57cec5SDimitry Andric        {"regions",
1940b57cec5SDimitry Andric         json::Object(
1950b57cec5SDimitry Andric             {{"count", int64_t(Summary.RegionCoverage.getNumRegions())},
1960b57cec5SDimitry Andric              {"covered", int64_t(Summary.RegionCoverage.getCovered())},
1970b57cec5SDimitry Andric              {"notcovered", int64_t(Summary.RegionCoverage.getNumRegions() -
1980b57cec5SDimitry Andric                                     Summary.RegionCoverage.getCovered())},
199e8d8bef9SDimitry Andric              {"percent", Summary.RegionCoverage.getPercentCovered()}})},
200e8d8bef9SDimitry Andric        {"branches",
201e8d8bef9SDimitry Andric         json::Object(
202e8d8bef9SDimitry Andric             {{"count", int64_t(Summary.BranchCoverage.getNumBranches())},
203e8d8bef9SDimitry Andric              {"covered", int64_t(Summary.BranchCoverage.getCovered())},
204e8d8bef9SDimitry Andric              {"notcovered", int64_t(Summary.BranchCoverage.getNumBranches() -
205e8d8bef9SDimitry Andric                                     Summary.BranchCoverage.getCovered())},
2065f757f3fSDimitry Andric              {"percent", Summary.BranchCoverage.getPercentCovered()}})},
2075f757f3fSDimitry Andric        {"mcdc",
2085f757f3fSDimitry Andric         json::Object(
2095f757f3fSDimitry Andric             {{"count", int64_t(Summary.MCDCCoverage.getNumPairs())},
2105f757f3fSDimitry Andric              {"covered", int64_t(Summary.MCDCCoverage.getCoveredPairs())},
2115f757f3fSDimitry Andric              {"notcovered", int64_t(Summary.MCDCCoverage.getNumPairs() -
2125f757f3fSDimitry Andric                                     Summary.MCDCCoverage.getCoveredPairs())},
2135f757f3fSDimitry Andric              {"percent", Summary.MCDCCoverage.getPercentCovered()}})}});
2140b57cec5SDimitry Andric }
2150b57cec5SDimitry Andric 
216e8d8bef9SDimitry Andric json::Array renderFileExpansions(const coverage::CoverageMapping &Coverage,
217e8d8bef9SDimitry Andric                                  const coverage::CoverageData &FileCoverage,
2180b57cec5SDimitry Andric                                  const FileCoverageSummary &FileReport) {
2190b57cec5SDimitry Andric   json::Array ExpansionArray;
2200b57cec5SDimitry Andric   for (const auto &Expansion : FileCoverage.getExpansions())
221e8d8bef9SDimitry Andric     ExpansionArray.push_back(renderExpansion(Coverage, Expansion));
2220b57cec5SDimitry Andric   return ExpansionArray;
2230b57cec5SDimitry Andric }
2240b57cec5SDimitry Andric 
2250b57cec5SDimitry Andric json::Array renderFileSegments(const coverage::CoverageData &FileCoverage,
2260b57cec5SDimitry Andric                                const FileCoverageSummary &FileReport) {
2270b57cec5SDimitry Andric   json::Array SegmentArray;
2280b57cec5SDimitry Andric   for (const auto &Segment : FileCoverage)
2290b57cec5SDimitry Andric     SegmentArray.push_back(renderSegment(Segment));
2300b57cec5SDimitry Andric   return SegmentArray;
2310b57cec5SDimitry Andric }
2320b57cec5SDimitry Andric 
233e8d8bef9SDimitry Andric json::Array renderFileBranches(const coverage::CoverageData &FileCoverage,
234e8d8bef9SDimitry Andric                                const FileCoverageSummary &FileReport) {
235e8d8bef9SDimitry Andric   json::Array BranchArray;
236e8d8bef9SDimitry Andric   for (const auto &Branch : FileCoverage.getBranches())
237e8d8bef9SDimitry Andric     BranchArray.push_back(renderBranch(Branch));
238e8d8bef9SDimitry Andric   return BranchArray;
239e8d8bef9SDimitry Andric }
240e8d8bef9SDimitry Andric 
2415f757f3fSDimitry Andric json::Array renderFileMCDC(const coverage::CoverageData &FileCoverage,
2425f757f3fSDimitry Andric                            const FileCoverageSummary &FileReport) {
2435f757f3fSDimitry Andric   json::Array MCDCRecordArray;
2445f757f3fSDimitry Andric   for (const auto &Record : FileCoverage.getMCDCRecords())
2455f757f3fSDimitry Andric     MCDCRecordArray.push_back(renderMCDCRecord(Record));
2465f757f3fSDimitry Andric   return MCDCRecordArray;
2475f757f3fSDimitry Andric }
2485f757f3fSDimitry Andric 
2490b57cec5SDimitry Andric json::Object renderFile(const coverage::CoverageMapping &Coverage,
2500b57cec5SDimitry Andric                         const std::string &Filename,
2510b57cec5SDimitry Andric                         const FileCoverageSummary &FileReport,
2520b57cec5SDimitry Andric                         const CoverageViewOptions &Options) {
2530b57cec5SDimitry Andric   json::Object File({{"filename", Filename}});
2540b57cec5SDimitry Andric   if (!Options.ExportSummaryOnly) {
2550b57cec5SDimitry Andric     // Calculate and render detailed coverage information for given file.
2560b57cec5SDimitry Andric     auto FileCoverage = Coverage.getCoverageForFile(Filename);
2570b57cec5SDimitry Andric     File["segments"] = renderFileSegments(FileCoverage, FileReport);
258e8d8bef9SDimitry Andric     File["branches"] = renderFileBranches(FileCoverage, FileReport);
2595f757f3fSDimitry Andric     File["mcdc_records"] = renderFileMCDC(FileCoverage, FileReport);
2600b57cec5SDimitry Andric     if (!Options.SkipExpansions) {
261e8d8bef9SDimitry Andric       File["expansions"] =
262e8d8bef9SDimitry Andric           renderFileExpansions(Coverage, FileCoverage, FileReport);
2630b57cec5SDimitry Andric     }
2640b57cec5SDimitry Andric   }
2650b57cec5SDimitry Andric   File["summary"] = renderSummary(FileReport);
2660b57cec5SDimitry Andric   return File;
2670b57cec5SDimitry Andric }
2680b57cec5SDimitry Andric 
2690b57cec5SDimitry Andric json::Array renderFiles(const coverage::CoverageMapping &Coverage,
2700b57cec5SDimitry Andric                         ArrayRef<std::string> SourceFiles,
2710b57cec5SDimitry Andric                         ArrayRef<FileCoverageSummary> FileReports,
2720b57cec5SDimitry Andric                         const CoverageViewOptions &Options) {
2735ffd83dbSDimitry Andric   ThreadPoolStrategy S = hardware_concurrency(Options.NumThreads);
2745ffd83dbSDimitry Andric   if (Options.NumThreads == 0) {
2755ffd83dbSDimitry Andric     // If NumThreads is not specified, create one thread for each input, up to
2765ffd83dbSDimitry Andric     // the number of hardware cores.
2775ffd83dbSDimitry Andric     S = heavyweight_hardware_concurrency(SourceFiles.size());
2785ffd83dbSDimitry Andric     S.Limit = true;
2790b57cec5SDimitry Andric   }
280*0fca6ea1SDimitry Andric   DefaultThreadPool Pool(S);
2810b57cec5SDimitry Andric   json::Array FileArray;
2820b57cec5SDimitry Andric   std::mutex FileArrayMutex;
2830b57cec5SDimitry Andric 
2840b57cec5SDimitry Andric   for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) {
2850b57cec5SDimitry Andric     auto &SourceFile = SourceFiles[I];
2860b57cec5SDimitry Andric     auto &FileReport = FileReports[I];
2870b57cec5SDimitry Andric     Pool.async([&] {
2880b57cec5SDimitry Andric       auto File = renderFile(Coverage, SourceFile, FileReport, Options);
2890b57cec5SDimitry Andric       {
2900b57cec5SDimitry Andric         std::lock_guard<std::mutex> Lock(FileArrayMutex);
2910b57cec5SDimitry Andric         FileArray.push_back(std::move(File));
2920b57cec5SDimitry Andric       }
2930b57cec5SDimitry Andric     });
2940b57cec5SDimitry Andric   }
2950b57cec5SDimitry Andric   Pool.wait();
2960b57cec5SDimitry Andric   return FileArray;
2970b57cec5SDimitry Andric }
2980b57cec5SDimitry Andric 
2990b57cec5SDimitry Andric json::Array renderFunctions(
3000b57cec5SDimitry Andric     const iterator_range<coverage::FunctionRecordIterator> &Functions) {
3010b57cec5SDimitry Andric   json::Array FunctionArray;
3020b57cec5SDimitry Andric   for (const auto &F : Functions)
3030b57cec5SDimitry Andric     FunctionArray.push_back(
3040b57cec5SDimitry Andric         json::Object({{"name", F.Name},
305480093f4SDimitry Andric                       {"count", clamp_uint64_to_int64(F.ExecutionCount)},
3060b57cec5SDimitry Andric                       {"regions", renderRegions(F.CountedRegions)},
307e8d8bef9SDimitry Andric                       {"branches", renderBranchRegions(F.CountedBranchRegions)},
3085f757f3fSDimitry Andric                       {"mcdc_records", renderMCDCRecords(F.MCDCRecords)},
3090b57cec5SDimitry Andric                       {"filenames", json::Array(F.Filenames)}}));
3100b57cec5SDimitry Andric   return FunctionArray;
3110b57cec5SDimitry Andric }
3120b57cec5SDimitry Andric 
3130b57cec5SDimitry Andric } // end anonymous namespace
3140b57cec5SDimitry Andric 
3150b57cec5SDimitry Andric void CoverageExporterJson::renderRoot(const CoverageFilters &IgnoreFilters) {
3160b57cec5SDimitry Andric   std::vector<std::string> SourceFiles;
3170b57cec5SDimitry Andric   for (StringRef SF : Coverage.getUniqueSourceFiles()) {
3180b57cec5SDimitry Andric     if (!IgnoreFilters.matchesFilename(SF))
3190b57cec5SDimitry Andric       SourceFiles.emplace_back(SF);
3200b57cec5SDimitry Andric   }
3210b57cec5SDimitry Andric   renderRoot(SourceFiles);
3220b57cec5SDimitry Andric }
3230b57cec5SDimitry Andric 
3240b57cec5SDimitry Andric void CoverageExporterJson::renderRoot(ArrayRef<std::string> SourceFiles) {
3250b57cec5SDimitry Andric   FileCoverageSummary Totals = FileCoverageSummary("Totals");
3260b57cec5SDimitry Andric   auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
3270b57cec5SDimitry Andric                                                         SourceFiles, Options);
3280b57cec5SDimitry Andric   auto Files = renderFiles(Coverage, SourceFiles, FileReports, Options);
3290b57cec5SDimitry Andric   // Sort files in order of their names.
330e8d8bef9SDimitry Andric   llvm::sort(Files, [](const json::Value &A, const json::Value &B) {
3310b57cec5SDimitry Andric     const json::Object *ObjA = A.getAsObject();
3320b57cec5SDimitry Andric     const json::Object *ObjB = B.getAsObject();
3330b57cec5SDimitry Andric     assert(ObjA != nullptr && "Value A was not an Object");
3340b57cec5SDimitry Andric     assert(ObjB != nullptr && "Value B was not an Object");
335bdd1243dSDimitry Andric     const StringRef FilenameA = *ObjA->getString("filename");
336bdd1243dSDimitry Andric     const StringRef FilenameB = *ObjB->getString("filename");
3370b57cec5SDimitry Andric     return FilenameA.compare(FilenameB) < 0;
3380b57cec5SDimitry Andric   });
3390b57cec5SDimitry Andric   auto Export = json::Object(
3400b57cec5SDimitry Andric       {{"files", std::move(Files)}, {"totals", renderSummary(Totals)}});
3410b57cec5SDimitry Andric   // Skip functions-level information  if necessary.
3420b57cec5SDimitry Andric   if (!Options.ExportSummaryOnly && !Options.SkipFunctions)
3430b57cec5SDimitry Andric     Export["functions"] = renderFunctions(Coverage.getCoveredFunctions());
3440b57cec5SDimitry Andric 
3450b57cec5SDimitry Andric   auto ExportArray = json::Array({std::move(Export)});
3460b57cec5SDimitry Andric 
3470b57cec5SDimitry Andric   OS << json::Object({{"version", LLVM_COVERAGE_EXPORT_JSON_STR},
3480b57cec5SDimitry Andric                       {"type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR},
3490b57cec5SDimitry Andric                       {"data", std::move(ExportArray)}});
3500b57cec5SDimitry Andric }
351