SourceCoverageViewText.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. //===- SourceCoverageViewText.cpp - A text-based code coverage view -------===//
  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. /// \file This file implements the text-based coverage renderer.
  10. ///
  11. //===----------------------------------------------------------------------===//
  12. #include "CoverageReport.h"
  13. #include "SourceCoverageViewText.h"
  14. #include "llvm/ADT/Optional.h"
  15. #include "llvm/ADT/SmallString.h"
  16. #include "llvm/ADT/StringExtras.h"
  17. using namespace llvm;
  18. Expected<CoveragePrinter::OwnedStream>
  19. CoveragePrinterText::createViewFile(StringRef Path, bool InToplevel) {
  20. return createOutputStream(Path, "txt", InToplevel);
  21. }
  22. void CoveragePrinterText::closeViewFile(OwnedStream OS) {
  23. OS->operator<<('\n');
  24. }
  25. Error CoveragePrinterText::createIndexFile(
  26. ArrayRef<std::string> SourceFiles, const CoverageMapping &Coverage,
  27. const CoverageFiltersMatchAll &Filters) {
  28. auto OSOrErr = createOutputStream("index", "txt", /*InToplevel=*/true);
  29. if (Error E = OSOrErr.takeError())
  30. return E;
  31. auto OS = std::move(OSOrErr.get());
  32. raw_ostream &OSRef = *OS.get();
  33. CoverageReport Report(Opts, Coverage);
  34. Report.renderFileReports(OSRef, SourceFiles, Filters);
  35. Opts.colored_ostream(OSRef, raw_ostream::CYAN) << "\n"
  36. << Opts.getLLVMVersionString();
  37. return Error::success();
  38. }
  39. namespace {
  40. static const unsigned LineCoverageColumnWidth = 7;
  41. static const unsigned LineNumberColumnWidth = 5;
  42. /// Get the width of the leading columns.
  43. unsigned getCombinedColumnWidth(const CoverageViewOptions &Opts) {
  44. return (Opts.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
  45. (Opts.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
  46. }
  47. /// The width of the line that is used to divide between the view and
  48. /// the subviews.
  49. unsigned getDividerWidth(const CoverageViewOptions &Opts) {
  50. return getCombinedColumnWidth(Opts) + 4;
  51. }
  52. } // anonymous namespace
  53. void SourceCoverageViewText::renderViewHeader(raw_ostream &) {}
  54. void SourceCoverageViewText::renderViewFooter(raw_ostream &) {}
  55. void SourceCoverageViewText::renderSourceName(raw_ostream &OS, bool WholeFile) {
  56. getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName()
  57. << ":\n";
  58. }
  59. void SourceCoverageViewText::renderLinePrefix(raw_ostream &OS,
  60. unsigned ViewDepth) {
  61. for (unsigned I = 0; I < ViewDepth; ++I)
  62. OS << " |";
  63. }
  64. void SourceCoverageViewText::renderLineSuffix(raw_ostream &, unsigned) {}
  65. void SourceCoverageViewText::renderViewDivider(raw_ostream &OS,
  66. unsigned ViewDepth) {
  67. assert(ViewDepth != 0 && "Cannot render divider at top level");
  68. renderLinePrefix(OS, ViewDepth - 1);
  69. OS.indent(2);
  70. unsigned Length = getDividerWidth(getOptions());
  71. for (unsigned I = 0; I < Length; ++I)
  72. OS << '-';
  73. OS << '\n';
  74. }
  75. void SourceCoverageViewText::renderLine(raw_ostream &OS, LineRef L,
  76. const LineCoverageStats &LCS,
  77. unsigned ExpansionCol,
  78. unsigned ViewDepth) {
  79. StringRef Line = L.Line;
  80. unsigned LineNumber = L.LineNo;
  81. auto *WrappedSegment = LCS.getWrappedSegment();
  82. CoverageSegmentArray Segments = LCS.getLineSegments();
  83. Optional<raw_ostream::Color> Highlight;
  84. SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
  85. // The first segment overlaps from a previous line, so we treat it specially.
  86. if (WrappedSegment && !WrappedSegment->IsGapRegion &&
  87. WrappedSegment->HasCount && WrappedSegment->Count == 0)
  88. Highlight = raw_ostream::RED;
  89. // Output each segment of the line, possibly highlighted.
  90. unsigned Col = 1;
  91. for (const auto *S : Segments) {
  92. unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);
  93. colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
  94. getOptions().Colors && Highlight, /*Bold=*/false,
  95. /*BG=*/true)
  96. << Line.substr(Col - 1, End - Col);
  97. if (getOptions().Debug && Highlight)
  98. HighlightedRanges.push_back(std::make_pair(Col, End));
  99. Col = End;
  100. if ((!S->IsGapRegion || (Highlight && *Highlight == raw_ostream::RED)) &&
  101. S->HasCount && S->Count == 0)
  102. Highlight = raw_ostream::RED;
  103. else if (Col == ExpansionCol)
  104. Highlight = raw_ostream::CYAN;
  105. else
  106. Highlight = None;
  107. }
  108. // Show the rest of the line.
  109. colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
  110. getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true)
  111. << Line.substr(Col - 1, Line.size() - Col + 1);
  112. OS << '\n';
  113. if (getOptions().Debug) {
  114. for (const auto &Range : HighlightedRanges)
  115. errs() << "Highlighted line " << LineNumber << ", " << Range.first
  116. << " -> " << Range.second << '\n';
  117. if (Highlight)
  118. errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n";
  119. }
  120. }
  121. void SourceCoverageViewText::renderLineCoverageColumn(
  122. raw_ostream &OS, const LineCoverageStats &Line) {
  123. if (!Line.isMapped()) {
  124. OS.indent(LineCoverageColumnWidth) << '|';
  125. return;
  126. }
  127. std::string C = formatCount(Line.getExecutionCount());
  128. OS.indent(LineCoverageColumnWidth - C.size());
  129. colored_ostream(OS, raw_ostream::MAGENTA,
  130. Line.hasMultipleRegions() && getOptions().Colors)
  131. << C;
  132. OS << '|';
  133. }
  134. void SourceCoverageViewText::renderLineNumberColumn(raw_ostream &OS,
  135. unsigned LineNo) {
  136. SmallString<32> Buffer;
  137. raw_svector_ostream BufferOS(Buffer);
  138. BufferOS << LineNo;
  139. auto Str = BufferOS.str();
  140. // Trim and align to the right.
  141. Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
  142. OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
  143. }
  144. void SourceCoverageViewText::renderRegionMarkers(raw_ostream &OS,
  145. const LineCoverageStats &Line,
  146. unsigned ViewDepth) {
  147. renderLinePrefix(OS, ViewDepth);
  148. OS.indent(getCombinedColumnWidth(getOptions()));
  149. CoverageSegmentArray Segments = Line.getLineSegments();
  150. // Just consider the segments which start *and* end on this line.
  151. if (Segments.size() > 1)
  152. Segments = Segments.drop_back();
  153. unsigned PrevColumn = 1;
  154. for (const auto *S : Segments) {
  155. if (!S->IsRegionEntry)
  156. continue;
  157. if (S->Count == Line.getExecutionCount())
  158. continue;
  159. // Skip to the new region.
  160. if (S->Col > PrevColumn)
  161. OS.indent(S->Col - PrevColumn);
  162. PrevColumn = S->Col + 1;
  163. std::string C = formatCount(S->Count);
  164. PrevColumn += C.size();
  165. OS << '^' << C;
  166. if (getOptions().Debug)
  167. errs() << "Marker at " << S->Line << ":" << S->Col << " = "
  168. << formatCount(S->Count) << "\n";
  169. }
  170. OS << '\n';
  171. }
  172. void SourceCoverageViewText::renderExpansionSite(raw_ostream &OS, LineRef L,
  173. const LineCoverageStats &LCS,
  174. unsigned ExpansionCol,
  175. unsigned ViewDepth) {
  176. renderLinePrefix(OS, ViewDepth);
  177. OS.indent(getCombinedColumnWidth(getOptions()) + (ViewDepth == 0 ? 0 : 1));
  178. renderLine(OS, L, LCS, ExpansionCol, ViewDepth);
  179. }
  180. void SourceCoverageViewText::renderExpansionView(raw_ostream &OS,
  181. ExpansionView &ESV,
  182. unsigned ViewDepth) {
  183. // Render the child subview.
  184. if (getOptions().Debug)
  185. errs() << "Expansion at line " << ESV.getLine() << ", " << ESV.getStartCol()
  186. << " -> " << ESV.getEndCol() << '\n';
  187. ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
  188. /*ShowTitle=*/false, ViewDepth + 1);
  189. }
  190. void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,
  191. InstantiationView &ISV,
  192. unsigned ViewDepth) {
  193. renderLinePrefix(OS, ViewDepth);
  194. OS << ' ';
  195. if (!ISV.View)
  196. getOptions().colored_ostream(OS, raw_ostream::RED)
  197. << "Unexecuted instantiation: " << ISV.FunctionName << "\n";
  198. else
  199. ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true,
  200. /*ShowTitle=*/false, ViewDepth);
  201. }
  202. void SourceCoverageViewText::renderTitle(raw_ostream &OS, StringRef Title) {
  203. if (getOptions().hasProjectTitle())
  204. getOptions().colored_ostream(OS, raw_ostream::CYAN)
  205. << getOptions().ProjectTitle << "\n";
  206. getOptions().colored_ostream(OS, raw_ostream::CYAN) << Title << "\n";
  207. if (getOptions().hasCreatedTime())
  208. getOptions().colored_ostream(OS, raw_ostream::CYAN)
  209. << getOptions().CreatedTimeStr << "\n";
  210. }
  211. void SourceCoverageViewText::renderTableHeader(raw_ostream &, unsigned,
  212. unsigned) {}