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