CoverageReport.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. //===- CoverageReport.cpp - Code coverage report -------------------------===//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. //
  10. // This class implements rendering of a code coverage report.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "CoverageReport.h"
  14. #include "RenderingSupport.h"
  15. #include "llvm/Support/FileSystem.h"
  16. #include "llvm/Support/Format.h"
  17. using namespace llvm;
  18. namespace {
  19. /// \brief Helper struct which prints trimmed and aligned columns.
  20. struct Column {
  21. enum TrimKind { NoTrim, LeftTrim, RightTrim };
  22. enum AlignmentKind { LeftAlignment, RightAlignment };
  23. StringRef Str;
  24. unsigned Width;
  25. TrimKind Trim;
  26. AlignmentKind Alignment;
  27. Column(StringRef Str, unsigned Width)
  28. : Str(Str), Width(Width), Trim(NoTrim), Alignment(LeftAlignment) {}
  29. Column &set(TrimKind Value) {
  30. Trim = Value;
  31. return *this;
  32. }
  33. Column &set(AlignmentKind Value) {
  34. Alignment = Value;
  35. return *this;
  36. }
  37. void render(raw_ostream &OS) const;
  38. };
  39. raw_ostream &operator<<(raw_ostream &OS, const Column &Value) {
  40. Value.render(OS);
  41. return OS;
  42. }
  43. }
  44. void Column::render(raw_ostream &OS) const {
  45. if (Str.size() <= Width) {
  46. if (Alignment == RightAlignment) {
  47. OS.indent(Width - Str.size());
  48. OS << Str;
  49. return;
  50. }
  51. OS << Str;
  52. OS.indent(Width - Str.size());
  53. return;
  54. }
  55. switch (Trim) {
  56. case NoTrim:
  57. OS << Str.substr(0, Width);
  58. break;
  59. case LeftTrim:
  60. OS << "..." << Str.substr(Str.size() - Width + 3);
  61. break;
  62. case RightTrim:
  63. OS << Str.substr(0, Width - 3) << "...";
  64. break;
  65. }
  66. }
  67. static Column column(StringRef Str, unsigned Width) {
  68. return Column(Str, Width);
  69. }
  70. template <typename T>
  71. static Column column(StringRef Str, unsigned Width, const T &Value) {
  72. return Column(Str, Width).set(Value);
  73. }
  74. static const unsigned FileReportColumns[] = {25, 10, 8, 8, 10, 10};
  75. static const unsigned FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8};
  76. /// \brief Prints a horizontal divider which spans across the given columns.
  77. template <typename T, size_t N>
  78. static void renderDivider(T (&Columns)[N], raw_ostream &OS) {
  79. unsigned Length = 0;
  80. for (unsigned I = 0; I < N; ++I)
  81. Length += Columns[I];
  82. for (unsigned I = 0; I < Length; ++I)
  83. OS << '-';
  84. }
  85. /// \brief Return the color which correponds to the coverage
  86. /// percentage of a certain metric.
  87. template <typename T>
  88. static raw_ostream::Colors determineCoveragePercentageColor(const T &Info) {
  89. if (Info.isFullyCovered())
  90. return raw_ostream::GREEN;
  91. return Info.getPercentCovered() >= 80.0 ? raw_ostream::YELLOW
  92. : raw_ostream::RED;
  93. }
  94. void CoverageReport::render(const FileCoverageSummary &File, raw_ostream &OS) {
  95. OS << column(File.Name, FileReportColumns[0], Column::LeftTrim)
  96. << format("%*u", FileReportColumns[1], (unsigned)File.RegionCoverage.NumRegions);
  97. Options.colored_ostream(OS, File.RegionCoverage.isFullyCovered()
  98. ? raw_ostream::GREEN
  99. : raw_ostream::RED)
  100. << format("%*u", FileReportColumns[2], (unsigned)File.RegionCoverage.NotCovered);
  101. Options.colored_ostream(OS,
  102. determineCoveragePercentageColor(File.RegionCoverage))
  103. << format("%*.2f", FileReportColumns[3] - 1,
  104. File.RegionCoverage.getPercentCovered()) << '%';
  105. OS << format("%*u", FileReportColumns[4],
  106. (unsigned)File.FunctionCoverage.NumFunctions);
  107. Options.colored_ostream(
  108. OS, determineCoveragePercentageColor(File.FunctionCoverage))
  109. << format("%*.2f", FileReportColumns[5] - 1,
  110. File.FunctionCoverage.getPercentCovered()) << '%';
  111. OS << "\n";
  112. }
  113. void CoverageReport::render(const FunctionCoverageSummary &Function,
  114. raw_ostream &OS) {
  115. OS << column(Function.Name, FunctionReportColumns[0], Column::RightTrim)
  116. << format("%*u", FunctionReportColumns[1],
  117. (unsigned)Function.RegionCoverage.NumRegions);
  118. Options.colored_ostream(OS, Function.RegionCoverage.isFullyCovered()
  119. ? raw_ostream::GREEN
  120. : raw_ostream::RED)
  121. << format("%*u", FunctionReportColumns[2],
  122. (unsigned)Function.RegionCoverage.NotCovered);
  123. Options.colored_ostream(
  124. OS, determineCoveragePercentageColor(Function.RegionCoverage))
  125. << format("%*.2f", FunctionReportColumns[3] - 1,
  126. Function.RegionCoverage.getPercentCovered()) << '%';
  127. OS << format("%*u", FunctionReportColumns[4],
  128. (unsigned)Function.LineCoverage.NumLines);
  129. Options.colored_ostream(OS, Function.LineCoverage.isFullyCovered()
  130. ? raw_ostream::GREEN
  131. : raw_ostream::RED)
  132. << format("%*u", FunctionReportColumns[5],
  133. (unsigned)Function.LineCoverage.NotCovered);
  134. Options.colored_ostream(
  135. OS, determineCoveragePercentageColor(Function.LineCoverage))
  136. << format("%*.2f", FunctionReportColumns[6] - 1,
  137. Function.LineCoverage.getPercentCovered()) << '%';
  138. OS << "\n";
  139. }
  140. void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
  141. raw_ostream &OS) {
  142. bool isFirst = true;
  143. for (StringRef Filename : Files) {
  144. if (isFirst)
  145. isFirst = false;
  146. else
  147. OS << "\n";
  148. OS << "File '" << Filename << "':\n";
  149. OS << column("Name", FunctionReportColumns[0])
  150. << column("Regions", FunctionReportColumns[1], Column::RightAlignment)
  151. << column("Miss", FunctionReportColumns[2], Column::RightAlignment)
  152. << column("Cover", FunctionReportColumns[3], Column::RightAlignment)
  153. << column("Lines", FunctionReportColumns[4], Column::RightAlignment)
  154. << column("Miss", FunctionReportColumns[5], Column::RightAlignment)
  155. << column("Cover", FunctionReportColumns[6], Column::RightAlignment);
  156. OS << "\n";
  157. renderDivider(FunctionReportColumns, OS);
  158. OS << "\n";
  159. FunctionCoverageSummary Totals("TOTAL");
  160. for (const auto &F : Coverage->getCoveredFunctions(Filename)) {
  161. FunctionCoverageSummary Function = FunctionCoverageSummary::get(F);
  162. ++Totals.ExecutionCount;
  163. Totals.RegionCoverage += Function.RegionCoverage;
  164. Totals.LineCoverage += Function.LineCoverage;
  165. render(Function, OS);
  166. }
  167. if (Totals.ExecutionCount) {
  168. renderDivider(FunctionReportColumns, OS);
  169. OS << "\n";
  170. render(Totals, OS);
  171. }
  172. }
  173. }
  174. void CoverageReport::renderFileReports(raw_ostream &OS) {
  175. OS << column("Filename", FileReportColumns[0])
  176. << column("Regions", FileReportColumns[1], Column::RightAlignment)
  177. << column("Miss", FileReportColumns[2], Column::RightAlignment)
  178. << column("Cover", FileReportColumns[3], Column::RightAlignment)
  179. << column("Functions", FileReportColumns[4], Column::RightAlignment)
  180. << column("Executed", FileReportColumns[5], Column::RightAlignment)
  181. << "\n";
  182. renderDivider(FileReportColumns, OS);
  183. OS << "\n";
  184. FileCoverageSummary Totals("TOTAL");
  185. for (StringRef Filename : Coverage->getUniqueSourceFiles()) {
  186. FileCoverageSummary Summary(Filename);
  187. for (const auto &F : Coverage->getCoveredFunctions(Filename)) {
  188. FunctionCoverageSummary Function = FunctionCoverageSummary::get(F);
  189. Summary.addFunction(Function);
  190. Totals.addFunction(Function);
  191. }
  192. render(Summary, OS);
  193. }
  194. renderDivider(FileReportColumns, OS);
  195. OS << "\n";
  196. render(Totals, OS);
  197. }