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