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