CoverageExporterJson.cpp 13 KB

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