SourceCoverageViewText.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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 "SourceCoverageViewText.h"
  14. #include "llvm/ADT/SmallString.h"
  15. #include "llvm/ADT/StringExtras.h"
  16. using namespace llvm;
  17. namespace {
  18. static const unsigned LineCoverageColumnWidth = 7;
  19. static const unsigned LineNumberColumnWidth = 5;
  20. /// \brief Get the width of the leading columns.
  21. unsigned getCombinedColumnWidth(const CoverageViewOptions &Opts) {
  22. return (Opts.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
  23. (Opts.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
  24. }
  25. /// \brief The width of the line that is used to divide between the view and
  26. /// the subviews.
  27. unsigned getDividerWidth(const CoverageViewOptions &Opts) {
  28. return getCombinedColumnWidth(Opts) + 4;
  29. }
  30. } // anonymous namespace
  31. void SourceCoverageViewText::renderSourceName(raw_ostream &OS) {
  32. getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName()
  33. << ":\n";
  34. }
  35. void SourceCoverageViewText::renderLinePrefix(raw_ostream &OS,
  36. unsigned ViewDepth) {
  37. for (unsigned I = 0; I < ViewDepth; ++I)
  38. OS << " |";
  39. }
  40. void SourceCoverageViewText::renderViewDivider(raw_ostream &OS,
  41. unsigned ViewDepth) {
  42. assert(ViewDepth != 0 && "Cannot render divider at top level");
  43. renderLinePrefix(OS, ViewDepth - 1);
  44. OS.indent(2);
  45. unsigned Length = getDividerWidth(getOptions());
  46. for (unsigned I = 0; I < Length; ++I)
  47. OS << '-';
  48. OS << '\n';
  49. }
  50. void SourceCoverageViewText::renderLine(
  51. raw_ostream &OS, LineRef L,
  52. const coverage::CoverageSegment *WrappedSegment,
  53. CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned ViewDepth) {
  54. StringRef Line = L.Line;
  55. unsigned LineNumber = L.LineNo;
  56. Optional<raw_ostream::Colors> Highlight;
  57. SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
  58. // The first segment overlaps from a previous line, so we treat it specially.
  59. if (WrappedSegment && WrappedSegment->HasCount && WrappedSegment->Count == 0)
  60. Highlight = raw_ostream::RED;
  61. // Output each segment of the line, possibly highlighted.
  62. unsigned Col = 1;
  63. for (const auto *S : Segments) {
  64. unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);
  65. colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
  66. getOptions().Colors && Highlight, /*Bold=*/false,
  67. /*BG=*/true)
  68. << Line.substr(Col - 1, End - Col);
  69. if (getOptions().Debug && Highlight)
  70. HighlightedRanges.push_back(std::make_pair(Col, End));
  71. Col = End;
  72. if (Col == ExpansionCol)
  73. Highlight = raw_ostream::CYAN;
  74. else if (S->HasCount && S->Count == 0)
  75. Highlight = raw_ostream::RED;
  76. else
  77. Highlight = None;
  78. }
  79. // Show the rest of the line.
  80. colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
  81. getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true)
  82. << Line.substr(Col - 1, Line.size() - Col + 1);
  83. OS << '\n';
  84. if (getOptions().Debug) {
  85. for (const auto &Range : HighlightedRanges)
  86. errs() << "Highlighted line " << LineNumber << ", " << Range.first
  87. << " -> " << Range.second << '\n';
  88. if (Highlight)
  89. errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n";
  90. }
  91. }
  92. void SourceCoverageViewText::renderLineCoverageColumn(
  93. raw_ostream &OS, const LineCoverageStats &Line) {
  94. if (!Line.isMapped()) {
  95. OS.indent(LineCoverageColumnWidth) << '|';
  96. return;
  97. }
  98. std::string C = formatCount(Line.ExecutionCount);
  99. OS.indent(LineCoverageColumnWidth - C.size());
  100. colored_ostream(OS, raw_ostream::MAGENTA,
  101. Line.hasMultipleRegions() && getOptions().Colors)
  102. << C;
  103. OS << '|';
  104. }
  105. void SourceCoverageViewText::renderLineNumberColumn(raw_ostream &OS,
  106. unsigned LineNo) {
  107. SmallString<32> Buffer;
  108. raw_svector_ostream BufferOS(Buffer);
  109. BufferOS << LineNo;
  110. auto Str = BufferOS.str();
  111. // Trim and align to the right.
  112. Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
  113. OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
  114. }
  115. void SourceCoverageViewText::renderRegionMarkers(
  116. raw_ostream &OS, CoverageSegmentArray Segments, unsigned ViewDepth) {
  117. renderLinePrefix(OS, ViewDepth);
  118. OS.indent(getCombinedColumnWidth(getOptions()));
  119. unsigned PrevColumn = 1;
  120. for (const auto *S : Segments) {
  121. if (!S->IsRegionEntry)
  122. continue;
  123. // Skip to the new region.
  124. if (S->Col > PrevColumn)
  125. OS.indent(S->Col - PrevColumn);
  126. PrevColumn = S->Col + 1;
  127. std::string C = formatCount(S->Count);
  128. PrevColumn += C.size();
  129. OS << '^' << C;
  130. }
  131. OS << '\n';
  132. if (getOptions().Debug)
  133. for (const auto *S : Segments)
  134. errs() << "Marker at " << S->Line << ":" << S->Col << " = "
  135. << formatCount(S->Count) << (S->IsRegionEntry ? "\n" : " (pop)\n");
  136. }
  137. unsigned SourceCoverageViewText::renderExpansionView(
  138. raw_ostream &OS, ExpansionView &ESV, Optional<LineRef> FirstLine,
  139. unsigned ExpansionCol, const coverage::CoverageSegment *WrappedSegment,
  140. CoverageSegmentArray LineSegments, unsigned ViewDepth) {
  141. unsigned NextExpansionCol = ExpansionCol;
  142. if (FirstLine.hasValue()) {
  143. // Re-render the current line and highlight the expansion range for
  144. // this subview.
  145. NextExpansionCol = ESV.getStartCol();
  146. renderLinePrefix(OS, ViewDepth);
  147. OS.indent(getCombinedColumnWidth(getOptions()) + (ViewDepth == 0 ? 0 : 1));
  148. renderLine(OS, *FirstLine, WrappedSegment, LineSegments, ExpansionCol,
  149. ViewDepth);
  150. renderViewDivider(OS, ViewDepth + 1);
  151. }
  152. // Render the child subview.
  153. if (getOptions().Debug)
  154. errs() << "Expansion at line " << ESV.getLine() << ", " << ESV.getStartCol()
  155. << " -> " << ESV.getEndCol() << '\n';
  156. ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
  157. ViewDepth + 1);
  158. return NextExpansionCol;
  159. }
  160. void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,
  161. InstantiationView &ISV,
  162. unsigned ViewDepth) {
  163. renderViewDivider(OS, ViewDepth);
  164. renderLinePrefix(OS, ViewDepth);
  165. OS << ' ';
  166. ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true, ViewDepth);
  167. }