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