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