SourceCoverageViewText.cpp 8.5 KB

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