xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-cov/SourceCoverageViewText.cpp (revision e8d8bef961a50d4dc22501cde4fb9fb0be1b2532)
10b57cec5SDimitry Andric //===- SourceCoverageViewText.cpp - A text-based code coverage view -------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric ///
90b57cec5SDimitry Andric /// \file This file implements the text-based coverage renderer.
100b57cec5SDimitry Andric ///
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "SourceCoverageViewText.h"
14*e8d8bef9SDimitry Andric #include "CoverageReport.h"
150b57cec5SDimitry Andric #include "llvm/ADT/Optional.h"
160b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
170b57cec5SDimitry Andric #include "llvm/ADT/StringExtras.h"
18*e8d8bef9SDimitry Andric #include "llvm/Support/Format.h"
190b57cec5SDimitry Andric 
200b57cec5SDimitry Andric using namespace llvm;
210b57cec5SDimitry Andric 
220b57cec5SDimitry Andric Expected<CoveragePrinter::OwnedStream>
230b57cec5SDimitry Andric CoveragePrinterText::createViewFile(StringRef Path, bool InToplevel) {
240b57cec5SDimitry Andric   return createOutputStream(Path, "txt", InToplevel);
250b57cec5SDimitry Andric }
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric void CoveragePrinterText::closeViewFile(OwnedStream OS) {
280b57cec5SDimitry Andric   OS->operator<<('\n');
290b57cec5SDimitry Andric }
300b57cec5SDimitry Andric 
310b57cec5SDimitry Andric Error CoveragePrinterText::createIndexFile(
320b57cec5SDimitry Andric     ArrayRef<std::string> SourceFiles, const CoverageMapping &Coverage,
330b57cec5SDimitry Andric     const CoverageFiltersMatchAll &Filters) {
340b57cec5SDimitry Andric   auto OSOrErr = createOutputStream("index", "txt", /*InToplevel=*/true);
350b57cec5SDimitry Andric   if (Error E = OSOrErr.takeError())
360b57cec5SDimitry Andric     return E;
370b57cec5SDimitry Andric   auto OS = std::move(OSOrErr.get());
380b57cec5SDimitry Andric   raw_ostream &OSRef = *OS.get();
390b57cec5SDimitry Andric 
400b57cec5SDimitry Andric   CoverageReport Report(Opts, Coverage);
410b57cec5SDimitry Andric   Report.renderFileReports(OSRef, SourceFiles, Filters);
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric   Opts.colored_ostream(OSRef, raw_ostream::CYAN) << "\n"
440b57cec5SDimitry Andric                                                  << Opts.getLLVMVersionString();
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric   return Error::success();
470b57cec5SDimitry Andric }
480b57cec5SDimitry Andric 
490b57cec5SDimitry Andric namespace {
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric static const unsigned LineCoverageColumnWidth = 7;
520b57cec5SDimitry Andric static const unsigned LineNumberColumnWidth = 5;
530b57cec5SDimitry Andric 
540b57cec5SDimitry Andric /// Get the width of the leading columns.
550b57cec5SDimitry Andric unsigned getCombinedColumnWidth(const CoverageViewOptions &Opts) {
560b57cec5SDimitry Andric   return (Opts.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
570b57cec5SDimitry Andric          (Opts.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
580b57cec5SDimitry Andric }
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric /// The width of the line that is used to divide between the view and
610b57cec5SDimitry Andric /// the subviews.
620b57cec5SDimitry Andric unsigned getDividerWidth(const CoverageViewOptions &Opts) {
630b57cec5SDimitry Andric   return getCombinedColumnWidth(Opts) + 4;
640b57cec5SDimitry Andric }
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric } // anonymous namespace
670b57cec5SDimitry Andric 
680b57cec5SDimitry Andric void SourceCoverageViewText::renderViewHeader(raw_ostream &) {}
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric void SourceCoverageViewText::renderViewFooter(raw_ostream &) {}
710b57cec5SDimitry Andric 
720b57cec5SDimitry Andric void SourceCoverageViewText::renderSourceName(raw_ostream &OS, bool WholeFile) {
730b57cec5SDimitry Andric   getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName()
740b57cec5SDimitry Andric                                                       << ":\n";
750b57cec5SDimitry Andric }
760b57cec5SDimitry Andric 
770b57cec5SDimitry Andric void SourceCoverageViewText::renderLinePrefix(raw_ostream &OS,
780b57cec5SDimitry Andric                                               unsigned ViewDepth) {
790b57cec5SDimitry Andric   for (unsigned I = 0; I < ViewDepth; ++I)
800b57cec5SDimitry Andric     OS << "  |";
810b57cec5SDimitry Andric }
820b57cec5SDimitry Andric 
830b57cec5SDimitry Andric void SourceCoverageViewText::renderLineSuffix(raw_ostream &, unsigned) {}
840b57cec5SDimitry Andric 
850b57cec5SDimitry Andric void SourceCoverageViewText::renderViewDivider(raw_ostream &OS,
860b57cec5SDimitry Andric                                                unsigned ViewDepth) {
870b57cec5SDimitry Andric   assert(ViewDepth != 0 && "Cannot render divider at top level");
880b57cec5SDimitry Andric   renderLinePrefix(OS, ViewDepth - 1);
890b57cec5SDimitry Andric   OS.indent(2);
900b57cec5SDimitry Andric   unsigned Length = getDividerWidth(getOptions());
910b57cec5SDimitry Andric   for (unsigned I = 0; I < Length; ++I)
920b57cec5SDimitry Andric     OS << '-';
930b57cec5SDimitry Andric   OS << '\n';
940b57cec5SDimitry Andric }
950b57cec5SDimitry Andric 
960b57cec5SDimitry Andric void SourceCoverageViewText::renderLine(raw_ostream &OS, LineRef L,
970b57cec5SDimitry Andric                                         const LineCoverageStats &LCS,
980b57cec5SDimitry Andric                                         unsigned ExpansionCol,
990b57cec5SDimitry Andric                                         unsigned ViewDepth) {
1000b57cec5SDimitry Andric   StringRef Line = L.Line;
1010b57cec5SDimitry Andric   unsigned LineNumber = L.LineNo;
1020b57cec5SDimitry Andric   auto *WrappedSegment = LCS.getWrappedSegment();
1030b57cec5SDimitry Andric   CoverageSegmentArray Segments = LCS.getLineSegments();
1040b57cec5SDimitry Andric 
1050b57cec5SDimitry Andric   Optional<raw_ostream::Colors> Highlight;
1060b57cec5SDimitry Andric   SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
1070b57cec5SDimitry Andric 
1080b57cec5SDimitry Andric   // The first segment overlaps from a previous line, so we treat it specially.
1090b57cec5SDimitry Andric   if (WrappedSegment && !WrappedSegment->IsGapRegion &&
1100b57cec5SDimitry Andric       WrappedSegment->HasCount && WrappedSegment->Count == 0)
1110b57cec5SDimitry Andric     Highlight = raw_ostream::RED;
1120b57cec5SDimitry Andric 
1130b57cec5SDimitry Andric   // Output each segment of the line, possibly highlighted.
1140b57cec5SDimitry Andric   unsigned Col = 1;
1150b57cec5SDimitry Andric   for (const auto *S : Segments) {
1160b57cec5SDimitry Andric     unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);
1170b57cec5SDimitry Andric     colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
1180b57cec5SDimitry Andric                     getOptions().Colors && Highlight, /*Bold=*/false,
1190b57cec5SDimitry Andric                     /*BG=*/true)
1200b57cec5SDimitry Andric         << Line.substr(Col - 1, End - Col);
1210b57cec5SDimitry Andric     if (getOptions().Debug && Highlight)
1220b57cec5SDimitry Andric       HighlightedRanges.push_back(std::make_pair(Col, End));
1230b57cec5SDimitry Andric     Col = End;
1240b57cec5SDimitry Andric     if ((!S->IsGapRegion || (Highlight && *Highlight == raw_ostream::RED)) &&
1250b57cec5SDimitry Andric         S->HasCount && S->Count == 0)
1260b57cec5SDimitry Andric       Highlight = raw_ostream::RED;
1270b57cec5SDimitry Andric     else if (Col == ExpansionCol)
1280b57cec5SDimitry Andric       Highlight = raw_ostream::CYAN;
1290b57cec5SDimitry Andric     else
1300b57cec5SDimitry Andric       Highlight = None;
1310b57cec5SDimitry Andric   }
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric   // Show the rest of the line.
1340b57cec5SDimitry Andric   colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
1350b57cec5SDimitry Andric                   getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true)
1360b57cec5SDimitry Andric       << Line.substr(Col - 1, Line.size() - Col + 1);
1370b57cec5SDimitry Andric   OS << '\n';
1380b57cec5SDimitry Andric 
1390b57cec5SDimitry Andric   if (getOptions().Debug) {
1400b57cec5SDimitry Andric     for (const auto &Range : HighlightedRanges)
1410b57cec5SDimitry Andric       errs() << "Highlighted line " << LineNumber << ", " << Range.first
1420b57cec5SDimitry Andric              << " -> " << Range.second << '\n';
1430b57cec5SDimitry Andric     if (Highlight)
1440b57cec5SDimitry Andric       errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n";
1450b57cec5SDimitry Andric   }
1460b57cec5SDimitry Andric }
1470b57cec5SDimitry Andric 
1480b57cec5SDimitry Andric void SourceCoverageViewText::renderLineCoverageColumn(
1490b57cec5SDimitry Andric     raw_ostream &OS, const LineCoverageStats &Line) {
1500b57cec5SDimitry Andric   if (!Line.isMapped()) {
1510b57cec5SDimitry Andric     OS.indent(LineCoverageColumnWidth) << '|';
1520b57cec5SDimitry Andric     return;
1530b57cec5SDimitry Andric   }
1540b57cec5SDimitry Andric   std::string C = formatCount(Line.getExecutionCount());
1550b57cec5SDimitry Andric   OS.indent(LineCoverageColumnWidth - C.size());
1560b57cec5SDimitry Andric   colored_ostream(OS, raw_ostream::MAGENTA,
1570b57cec5SDimitry Andric                   Line.hasMultipleRegions() && getOptions().Colors)
1580b57cec5SDimitry Andric       << C;
1590b57cec5SDimitry Andric   OS << '|';
1600b57cec5SDimitry Andric }
1610b57cec5SDimitry Andric 
1620b57cec5SDimitry Andric void SourceCoverageViewText::renderLineNumberColumn(raw_ostream &OS,
1630b57cec5SDimitry Andric                                                     unsigned LineNo) {
1640b57cec5SDimitry Andric   SmallString<32> Buffer;
1650b57cec5SDimitry Andric   raw_svector_ostream BufferOS(Buffer);
1660b57cec5SDimitry Andric   BufferOS << LineNo;
1670b57cec5SDimitry Andric   auto Str = BufferOS.str();
1680b57cec5SDimitry Andric   // Trim and align to the right.
1690b57cec5SDimitry Andric   Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
1700b57cec5SDimitry Andric   OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
1710b57cec5SDimitry Andric }
1720b57cec5SDimitry Andric 
1730b57cec5SDimitry Andric void SourceCoverageViewText::renderRegionMarkers(raw_ostream &OS,
1740b57cec5SDimitry Andric                                                  const LineCoverageStats &Line,
1750b57cec5SDimitry Andric                                                  unsigned ViewDepth) {
1760b57cec5SDimitry Andric   renderLinePrefix(OS, ViewDepth);
1770b57cec5SDimitry Andric   OS.indent(getCombinedColumnWidth(getOptions()));
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric   CoverageSegmentArray Segments = Line.getLineSegments();
1800b57cec5SDimitry Andric 
1810b57cec5SDimitry Andric   // Just consider the segments which start *and* end on this line.
1820b57cec5SDimitry Andric   if (Segments.size() > 1)
1830b57cec5SDimitry Andric     Segments = Segments.drop_back();
1840b57cec5SDimitry Andric 
1850b57cec5SDimitry Andric   unsigned PrevColumn = 1;
1860b57cec5SDimitry Andric   for (const auto *S : Segments) {
1870b57cec5SDimitry Andric     if (!S->IsRegionEntry)
1880b57cec5SDimitry Andric       continue;
1890b57cec5SDimitry Andric     if (S->Count == Line.getExecutionCount())
1900b57cec5SDimitry Andric       continue;
1910b57cec5SDimitry Andric     // Skip to the new region.
1920b57cec5SDimitry Andric     if (S->Col > PrevColumn)
1930b57cec5SDimitry Andric       OS.indent(S->Col - PrevColumn);
1940b57cec5SDimitry Andric     PrevColumn = S->Col + 1;
1950b57cec5SDimitry Andric     std::string C = formatCount(S->Count);
1960b57cec5SDimitry Andric     PrevColumn += C.size();
1970b57cec5SDimitry Andric     OS << '^' << C;
1980b57cec5SDimitry Andric 
1990b57cec5SDimitry Andric     if (getOptions().Debug)
2000b57cec5SDimitry Andric       errs() << "Marker at " << S->Line << ":" << S->Col << " = "
2010b57cec5SDimitry Andric             << formatCount(S->Count) << "\n";
2020b57cec5SDimitry Andric   }
2030b57cec5SDimitry Andric   OS << '\n';
2040b57cec5SDimitry Andric }
2050b57cec5SDimitry Andric 
2060b57cec5SDimitry Andric void SourceCoverageViewText::renderExpansionSite(raw_ostream &OS, LineRef L,
2070b57cec5SDimitry Andric                                                  const LineCoverageStats &LCS,
2080b57cec5SDimitry Andric                                                  unsigned ExpansionCol,
2090b57cec5SDimitry Andric                                                  unsigned ViewDepth) {
2100b57cec5SDimitry Andric   renderLinePrefix(OS, ViewDepth);
2110b57cec5SDimitry Andric   OS.indent(getCombinedColumnWidth(getOptions()) + (ViewDepth == 0 ? 0 : 1));
2120b57cec5SDimitry Andric   renderLine(OS, L, LCS, ExpansionCol, ViewDepth);
2130b57cec5SDimitry Andric }
2140b57cec5SDimitry Andric 
2150b57cec5SDimitry Andric void SourceCoverageViewText::renderExpansionView(raw_ostream &OS,
2160b57cec5SDimitry Andric                                                  ExpansionView &ESV,
2170b57cec5SDimitry Andric                                                  unsigned ViewDepth) {
2180b57cec5SDimitry Andric   // Render the child subview.
2190b57cec5SDimitry Andric   if (getOptions().Debug)
2200b57cec5SDimitry Andric     errs() << "Expansion at line " << ESV.getLine() << ", " << ESV.getStartCol()
2210b57cec5SDimitry Andric            << " -> " << ESV.getEndCol() << '\n';
2220b57cec5SDimitry Andric   ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
2230b57cec5SDimitry Andric                   /*ShowTitle=*/false, ViewDepth + 1);
2240b57cec5SDimitry Andric }
2250b57cec5SDimitry Andric 
226*e8d8bef9SDimitry Andric void SourceCoverageViewText::renderBranchView(raw_ostream &OS, BranchView &BRV,
227*e8d8bef9SDimitry Andric                                               unsigned ViewDepth) {
228*e8d8bef9SDimitry Andric   // Render the child subview.
229*e8d8bef9SDimitry Andric   if (getOptions().Debug)
230*e8d8bef9SDimitry Andric     errs() << "Branch at line " << BRV.getLine() << '\n';
231*e8d8bef9SDimitry Andric 
232*e8d8bef9SDimitry Andric   for (const auto &R : BRV.Regions) {
233*e8d8bef9SDimitry Andric     double TruePercent = 0.0;
234*e8d8bef9SDimitry Andric     double FalsePercent = 0.0;
235*e8d8bef9SDimitry Andric     unsigned Total = R.ExecutionCount + R.FalseExecutionCount;
236*e8d8bef9SDimitry Andric 
237*e8d8bef9SDimitry Andric     if (!getOptions().ShowBranchCounts && Total != 0) {
238*e8d8bef9SDimitry Andric       TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0;
239*e8d8bef9SDimitry Andric       FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0;
240*e8d8bef9SDimitry Andric     }
241*e8d8bef9SDimitry Andric 
242*e8d8bef9SDimitry Andric     renderLinePrefix(OS, ViewDepth);
243*e8d8bef9SDimitry Andric     OS << "  Branch (" << R.LineStart << ":" << R.ColumnStart << "): [";
244*e8d8bef9SDimitry Andric 
245*e8d8bef9SDimitry Andric     if (R.Folded) {
246*e8d8bef9SDimitry Andric       OS << "Folded - Ignored]\n";
247*e8d8bef9SDimitry Andric       continue;
248*e8d8bef9SDimitry Andric     }
249*e8d8bef9SDimitry Andric 
250*e8d8bef9SDimitry Andric     colored_ostream(OS, raw_ostream::RED,
251*e8d8bef9SDimitry Andric                     getOptions().Colors && !R.ExecutionCount,
252*e8d8bef9SDimitry Andric                     /*Bold=*/false, /*BG=*/true)
253*e8d8bef9SDimitry Andric         << "True";
254*e8d8bef9SDimitry Andric 
255*e8d8bef9SDimitry Andric     if (getOptions().ShowBranchCounts)
256*e8d8bef9SDimitry Andric       OS << ": " << formatCount(R.ExecutionCount) << ", ";
257*e8d8bef9SDimitry Andric     else
258*e8d8bef9SDimitry Andric       OS << ": " << format("%0.2f", TruePercent) << "%, ";
259*e8d8bef9SDimitry Andric 
260*e8d8bef9SDimitry Andric     colored_ostream(OS, raw_ostream::RED,
261*e8d8bef9SDimitry Andric                     getOptions().Colors && !R.FalseExecutionCount,
262*e8d8bef9SDimitry Andric                     /*Bold=*/false, /*BG=*/true)
263*e8d8bef9SDimitry Andric         << "False";
264*e8d8bef9SDimitry Andric 
265*e8d8bef9SDimitry Andric     if (getOptions().ShowBranchCounts)
266*e8d8bef9SDimitry Andric       OS << ": " << formatCount(R.FalseExecutionCount);
267*e8d8bef9SDimitry Andric     else
268*e8d8bef9SDimitry Andric       OS << ": " << format("%0.2f", FalsePercent) << "%";
269*e8d8bef9SDimitry Andric     OS << "]\n";
270*e8d8bef9SDimitry Andric   }
271*e8d8bef9SDimitry Andric }
272*e8d8bef9SDimitry Andric 
2730b57cec5SDimitry Andric void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,
2740b57cec5SDimitry Andric                                                      InstantiationView &ISV,
2750b57cec5SDimitry Andric                                                      unsigned ViewDepth) {
2760b57cec5SDimitry Andric   renderLinePrefix(OS, ViewDepth);
2770b57cec5SDimitry Andric   OS << ' ';
2780b57cec5SDimitry Andric   if (!ISV.View)
2790b57cec5SDimitry Andric     getOptions().colored_ostream(OS, raw_ostream::RED)
2800b57cec5SDimitry Andric         << "Unexecuted instantiation: " << ISV.FunctionName << "\n";
2810b57cec5SDimitry Andric   else
2820b57cec5SDimitry Andric     ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true,
2830b57cec5SDimitry Andric                     /*ShowTitle=*/false, ViewDepth);
2840b57cec5SDimitry Andric }
2850b57cec5SDimitry Andric 
2860b57cec5SDimitry Andric void SourceCoverageViewText::renderTitle(raw_ostream &OS, StringRef Title) {
2870b57cec5SDimitry Andric   if (getOptions().hasProjectTitle())
2880b57cec5SDimitry Andric     getOptions().colored_ostream(OS, raw_ostream::CYAN)
2890b57cec5SDimitry Andric         << getOptions().ProjectTitle << "\n";
2900b57cec5SDimitry Andric 
2910b57cec5SDimitry Andric   getOptions().colored_ostream(OS, raw_ostream::CYAN) << Title << "\n";
2920b57cec5SDimitry Andric 
2930b57cec5SDimitry Andric   if (getOptions().hasCreatedTime())
2940b57cec5SDimitry Andric     getOptions().colored_ostream(OS, raw_ostream::CYAN)
2950b57cec5SDimitry Andric         << getOptions().CreatedTimeStr << "\n";
2960b57cec5SDimitry Andric }
2970b57cec5SDimitry Andric 
2980b57cec5SDimitry Andric void SourceCoverageViewText::renderTableHeader(raw_ostream &, unsigned,
2990b57cec5SDimitry Andric                                                unsigned) {}
300