CoverageExporterJson.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. //===- CoverageExporterJson.cpp - Code coverage export --------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file implements export of code coverage data to JSON.
  10. //
  11. //===----------------------------------------------------------------------===//
  12. //===----------------------------------------------------------------------===//
  13. //
  14. // The json code coverage export follows the following format
  15. // Root: dict => Root Element containing metadata
  16. // -- Data: array => Homogeneous array of one or more export objects
  17. // -- Export: dict => Json representation of one CoverageMapping
  18. // -- Files: array => List of objects describing coverage for files
  19. // -- File: dict => Coverage for a single file
  20. // -- Branches: array => List of Branches in the file
  21. // -- Branch: dict => Describes a branch of the file with counters
  22. // -- Segments: array => List of Segments contained in the file
  23. // -- Segment: dict => Describes a segment of the file with a counter
  24. // -- Expansions: array => List of expansion records
  25. // -- Expansion: dict => Object that descibes a single expansion
  26. // -- CountedRegion: dict => The region to be expanded
  27. // -- TargetRegions: array => List of Regions in the expansion
  28. // -- CountedRegion: dict => Single Region in the expansion
  29. // -- Branches: array => List of Branches in the expansion
  30. // -- Branch: dict => Describes a branch in expansion and counters
  31. // -- Summary: dict => Object summarizing the coverage for this file
  32. // -- LineCoverage: dict => Object summarizing line coverage
  33. // -- FunctionCoverage: dict => Object summarizing function coverage
  34. // -- RegionCoverage: dict => Object summarizing region coverage
  35. // -- BranchCoverage: dict => Object summarizing branch coverage
  36. // -- Functions: array => List of objects describing coverage for functions
  37. // -- Function: dict => Coverage info for a single function
  38. // -- Filenames: array => List of filenames that the function relates to
  39. // -- Summary: dict => Object summarizing the coverage for the entire binary
  40. // -- LineCoverage: dict => Object summarizing line coverage
  41. // -- FunctionCoverage: dict => Object summarizing function coverage
  42. // -- InstantiationCoverage: dict => Object summarizing inst. coverage
  43. // -- RegionCoverage: dict => Object summarizing region coverage
  44. // -- BranchCoverage: dict => Object summarizing branch coverage
  45. //
  46. //===----------------------------------------------------------------------===//
  47. #include "CoverageExporterJson.h"
  48. #include "CoverageReport.h"
  49. #include "llvm/ADT/Optional.h"
  50. #include "llvm/ADT/StringRef.h"
  51. #include "llvm/Support/JSON.h"
  52. #include "llvm/Support/ThreadPool.h"
  53. #include "llvm/Support/Threading.h"
  54. #include <algorithm>
  55. #include <limits>
  56. #include <mutex>
  57. #include <utility>
  58. /// The semantic version combined as a string.
  59. #define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.1"
  60. /// Unique type identifier for JSON coverage export.
  61. #define LLVM_COVERAGE_EXPORT_JSON_TYPE_STR "llvm.coverage.json.export"
  62. using namespace llvm;
  63. namespace {
  64. // The JSON library accepts int64_t, but profiling counts are stored as uint64_t.
  65. // Therefore we need to explicitly convert from unsigned to signed, since a naive
  66. // cast is implementation-defined behavior when the unsigned value cannot be
  67. // represented as a signed value. We choose to clamp the values to preserve the
  68. // invariant that counts are always >= 0.
  69. int64_t clamp_uint64_to_int64(uint64_t u) {
  70. return std::min(u, static_cast<uint64_t>(std::numeric_limits<int64_t>::max()));
  71. }
  72. json::Array renderSegment(const coverage::CoverageSegment &Segment) {
  73. return json::Array({Segment.Line, Segment.Col,
  74. clamp_uint64_to_int64(Segment.Count), Segment.HasCount,
  75. Segment.IsRegionEntry, Segment.IsGapRegion});
  76. }
  77. json::Array renderRegion(const coverage::CountedRegion &Region) {
  78. return json::Array({Region.LineStart, Region.ColumnStart, Region.LineEnd,
  79. Region.ColumnEnd, clamp_uint64_to_int64(Region.ExecutionCount),
  80. Region.FileID, Region.ExpandedFileID,
  81. int64_t(Region.Kind)});
  82. }
  83. json::Array renderBranch(const coverage::CountedRegion &Region) {
  84. return json::Array(
  85. {Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd,
  86. clamp_uint64_to_int64(Region.ExecutionCount),
  87. clamp_uint64_to_int64(Region.FalseExecutionCount), Region.FileID,
  88. Region.ExpandedFileID, int64_t(Region.Kind)});
  89. }
  90. json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) {
  91. json::Array RegionArray;
  92. for (const auto &Region : Regions)
  93. RegionArray.push_back(renderRegion(Region));
  94. return RegionArray;
  95. }
  96. json::Array renderBranchRegions(ArrayRef<coverage::CountedRegion> Regions) {
  97. json::Array RegionArray;
  98. for (const auto &Region : Regions)
  99. if (!Region.Folded)
  100. RegionArray.push_back(renderBranch(Region));
  101. return RegionArray;
  102. }
  103. std::vector<llvm::coverage::CountedRegion>
  104. collectNestedBranches(const coverage::CoverageMapping &Coverage,
  105. ArrayRef<llvm::coverage::ExpansionRecord> Expansions) {
  106. std::vector<llvm::coverage::CountedRegion> Branches;
  107. for (const auto &Expansion : Expansions) {
  108. auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
  109. // Recursively collect branches from nested expansions.
  110. auto NestedExpansions = ExpansionCoverage.getExpansions();
  111. auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions);
  112. append_range(Branches, NestedExBranches);
  113. // Add branches from this level of expansion.
  114. auto ExBranches = ExpansionCoverage.getBranches();
  115. for (auto B : ExBranches)
  116. if (B.FileID == Expansion.FileID)
  117. Branches.push_back(B);
  118. }
  119. return Branches;
  120. }
  121. json::Object renderExpansion(const coverage::CoverageMapping &Coverage,
  122. const coverage::ExpansionRecord &Expansion) {
  123. std::vector<llvm::coverage::ExpansionRecord> Expansions = {Expansion};
  124. return json::Object(
  125. {{"filenames", json::Array(Expansion.Function.Filenames)},
  126. // Mark the beginning and end of this expansion in the source file.
  127. {"source_region", renderRegion(Expansion.Region)},
  128. // Enumerate the coverage information for the expansion.
  129. {"target_regions", renderRegions(Expansion.Function.CountedRegions)},
  130. // Enumerate the branch coverage information for the expansion.
  131. {"branches",
  132. renderBranchRegions(collectNestedBranches(Coverage, Expansions))}});
  133. }
  134. json::Object renderSummary(const FileCoverageSummary &Summary) {
  135. return json::Object(
  136. {{"lines",
  137. json::Object({{"count", int64_t(Summary.LineCoverage.getNumLines())},
  138. {"covered", int64_t(Summary.LineCoverage.getCovered())},
  139. {"percent", Summary.LineCoverage.getPercentCovered()}})},
  140. {"functions",
  141. json::Object(
  142. {{"count", int64_t(Summary.FunctionCoverage.getNumFunctions())},
  143. {"covered", int64_t(Summary.FunctionCoverage.getExecuted())},
  144. {"percent", Summary.FunctionCoverage.getPercentCovered()}})},
  145. {"instantiations",
  146. json::Object(
  147. {{"count",
  148. int64_t(Summary.InstantiationCoverage.getNumFunctions())},
  149. {"covered", int64_t(Summary.InstantiationCoverage.getExecuted())},
  150. {"percent", Summary.InstantiationCoverage.getPercentCovered()}})},
  151. {"regions",
  152. json::Object(
  153. {{"count", int64_t(Summary.RegionCoverage.getNumRegions())},
  154. {"covered", int64_t(Summary.RegionCoverage.getCovered())},
  155. {"notcovered", int64_t(Summary.RegionCoverage.getNumRegions() -
  156. Summary.RegionCoverage.getCovered())},
  157. {"percent", Summary.RegionCoverage.getPercentCovered()}})},
  158. {"branches",
  159. json::Object(
  160. {{"count", int64_t(Summary.BranchCoverage.getNumBranches())},
  161. {"covered", int64_t(Summary.BranchCoverage.getCovered())},
  162. {"notcovered", int64_t(Summary.BranchCoverage.getNumBranches() -
  163. Summary.BranchCoverage.getCovered())},
  164. {"percent", Summary.BranchCoverage.getPercentCovered()}})}});
  165. }
  166. json::Array renderFileExpansions(const coverage::CoverageMapping &Coverage,
  167. const coverage::CoverageData &FileCoverage,
  168. const FileCoverageSummary &FileReport) {
  169. json::Array ExpansionArray;
  170. for (const auto &Expansion : FileCoverage.getExpansions())
  171. ExpansionArray.push_back(renderExpansion(Coverage, Expansion));
  172. return ExpansionArray;
  173. }
  174. json::Array renderFileSegments(const coverage::CoverageData &FileCoverage,
  175. const FileCoverageSummary &FileReport) {
  176. json::Array SegmentArray;
  177. for (const auto &Segment : FileCoverage)
  178. SegmentArray.push_back(renderSegment(Segment));
  179. return SegmentArray;
  180. }
  181. json::Array renderFileBranches(const coverage::CoverageData &FileCoverage,
  182. const FileCoverageSummary &FileReport) {
  183. json::Array BranchArray;
  184. for (const auto &Branch : FileCoverage.getBranches())
  185. BranchArray.push_back(renderBranch(Branch));
  186. return BranchArray;
  187. }
  188. json::Object renderFile(const coverage::CoverageMapping &Coverage,
  189. const std::string &Filename,
  190. const FileCoverageSummary &FileReport,
  191. const CoverageViewOptions &Options) {
  192. json::Object File({{"filename", Filename}});
  193. if (!Options.ExportSummaryOnly) {
  194. // Calculate and render detailed coverage information for given file.
  195. auto FileCoverage = Coverage.getCoverageForFile(Filename);
  196. File["segments"] = renderFileSegments(FileCoverage, FileReport);
  197. File["branches"] = renderFileBranches(FileCoverage, FileReport);
  198. if (!Options.SkipExpansions) {
  199. File["expansions"] =
  200. renderFileExpansions(Coverage, FileCoverage, FileReport);
  201. }
  202. }
  203. File["summary"] = renderSummary(FileReport);
  204. return File;
  205. }
  206. json::Array renderFiles(const coverage::CoverageMapping &Coverage,
  207. ArrayRef<std::string> SourceFiles,
  208. ArrayRef<FileCoverageSummary> FileReports,
  209. const CoverageViewOptions &Options) {
  210. ThreadPoolStrategy S = hardware_concurrency(Options.NumThreads);
  211. if (Options.NumThreads == 0) {
  212. // If NumThreads is not specified, create one thread for each input, up to
  213. // the number of hardware cores.
  214. S = heavyweight_hardware_concurrency(SourceFiles.size());
  215. S.Limit = true;
  216. }
  217. ThreadPool Pool(S);
  218. json::Array FileArray;
  219. std::mutex FileArrayMutex;
  220. for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) {
  221. auto &SourceFile = SourceFiles[I];
  222. auto &FileReport = FileReports[I];
  223. Pool.async([&] {
  224. auto File = renderFile(Coverage, SourceFile, FileReport, Options);
  225. {
  226. std::lock_guard<std::mutex> Lock(FileArrayMutex);
  227. FileArray.push_back(std::move(File));
  228. }
  229. });
  230. }
  231. Pool.wait();
  232. return FileArray;
  233. }
  234. json::Array renderFunctions(
  235. const iterator_range<coverage::FunctionRecordIterator> &Functions) {
  236. json::Array FunctionArray;
  237. for (const auto &F : Functions)
  238. FunctionArray.push_back(
  239. json::Object({{"name", F.Name},
  240. {"count", clamp_uint64_to_int64(F.ExecutionCount)},
  241. {"regions", renderRegions(F.CountedRegions)},
  242. {"branches", renderBranchRegions(F.CountedBranchRegions)},
  243. {"filenames", json::Array(F.Filenames)}}));
  244. return FunctionArray;
  245. }
  246. } // end anonymous namespace
  247. void CoverageExporterJson::renderRoot(const CoverageFilters &IgnoreFilters) {
  248. std::vector<std::string> SourceFiles;
  249. for (StringRef SF : Coverage.getUniqueSourceFiles()) {
  250. if (!IgnoreFilters.matchesFilename(SF))
  251. SourceFiles.emplace_back(SF);
  252. }
  253. renderRoot(SourceFiles);
  254. }
  255. void CoverageExporterJson::renderRoot(ArrayRef<std::string> SourceFiles) {
  256. FileCoverageSummary Totals = FileCoverageSummary("Totals");
  257. auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
  258. SourceFiles, Options);
  259. auto Files = renderFiles(Coverage, SourceFiles, FileReports, Options);
  260. // Sort files in order of their names.
  261. llvm::sort(Files, [](const json::Value &A, const json::Value &B) {
  262. const json::Object *ObjA = A.getAsObject();
  263. const json::Object *ObjB = B.getAsObject();
  264. assert(ObjA != nullptr && "Value A was not an Object");
  265. assert(ObjB != nullptr && "Value B was not an Object");
  266. const StringRef FilenameA = ObjA->getString("filename").getValue();
  267. const StringRef FilenameB = ObjB->getString("filename").getValue();
  268. return FilenameA.compare(FilenameB) < 0;
  269. });
  270. auto Export = json::Object(
  271. {{"files", std::move(Files)}, {"totals", renderSummary(Totals)}});
  272. // Skip functions-level information if necessary.
  273. if (!Options.ExportSummaryOnly && !Options.SkipFunctions)
  274. Export["functions"] = renderFunctions(Coverage.getCoveredFunctions());
  275. auto ExportArray = json::Array({std::move(Export)});
  276. OS << json::Object({{"version", LLVM_COVERAGE_EXPORT_JSON_STR},
  277. {"type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR},
  278. {"data", std::move(ExportArray)}});
  279. }