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