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