Home | History | Annotate | Download | only in llvm-cov
      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, const CoverageMapping &Coverage,
     33     const CoverageFiltersMatchAll &Filters) {
     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, Filters);
     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 /// 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 /// 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(raw_ostream &OS, LineRef L,
     97                                         const LineCoverageStats &LCS,
     98                                         unsigned ExpansionCol,
     99                                         unsigned ViewDepth) {
    100   StringRef Line = L.Line;
    101   unsigned LineNumber = L.LineNo;
    102   auto *WrappedSegment = LCS.getWrappedSegment();
    103   CoverageSegmentArray Segments = LCS.getLineSegments();
    104 
    105   Optional<raw_ostream::Colors> Highlight;
    106   SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
    107 
    108   // The first segment overlaps from a previous line, so we treat it specially.
    109   if (WrappedSegment && !WrappedSegment->IsGapRegion &&
    110       WrappedSegment->HasCount && WrappedSegment->Count == 0)
    111     Highlight = raw_ostream::RED;
    112 
    113   // Output each segment of the line, possibly highlighted.
    114   unsigned Col = 1;
    115   for (const auto *S : Segments) {
    116     unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);
    117     colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
    118                     getOptions().Colors && Highlight, /*Bold=*/false,
    119                     /*BG=*/true)
    120         << Line.substr(Col - 1, End - Col);
    121     if (getOptions().Debug && Highlight)
    122       HighlightedRanges.push_back(std::make_pair(Col, End));
    123     Col = End;
    124     if ((!S->IsGapRegion || (Highlight && *Highlight == raw_ostream::RED)) &&
    125         S->HasCount && S->Count == 0)
    126       Highlight = raw_ostream::RED;
    127     else if (Col == ExpansionCol)
    128       Highlight = raw_ostream::CYAN;
    129     else
    130       Highlight = None;
    131   }
    132 
    133   // Show the rest of the line.
    134   colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
    135                   getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true)
    136       << Line.substr(Col - 1, Line.size() - Col + 1);
    137   OS << '\n';
    138 
    139   if (getOptions().Debug) {
    140     for (const auto &Range : HighlightedRanges)
    141       errs() << "Highlighted line " << LineNumber << ", " << Range.first
    142              << " -> " << Range.second << '\n';
    143     if (Highlight)
    144       errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n";
    145   }
    146 }
    147 
    148 void SourceCoverageViewText::renderLineCoverageColumn(
    149     raw_ostream &OS, const LineCoverageStats &Line) {
    150   if (!Line.isMapped()) {
    151     OS.indent(LineCoverageColumnWidth) << '|';
    152     return;
    153   }
    154   std::string C = formatCount(Line.getExecutionCount());
    155   OS.indent(LineCoverageColumnWidth - C.size());
    156   colored_ostream(OS, raw_ostream::MAGENTA,
    157                   Line.hasMultipleRegions() && getOptions().Colors)
    158       << C;
    159   OS << '|';
    160 }
    161 
    162 void SourceCoverageViewText::renderLineNumberColumn(raw_ostream &OS,
    163                                                     unsigned LineNo) {
    164   SmallString<32> Buffer;
    165   raw_svector_ostream BufferOS(Buffer);
    166   BufferOS << LineNo;
    167   auto Str = BufferOS.str();
    168   // Trim and align to the right.
    169   Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
    170   OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
    171 }
    172 
    173 void SourceCoverageViewText::renderRegionMarkers(raw_ostream &OS,
    174                                                  const LineCoverageStats &Line,
    175                                                  unsigned ViewDepth) {
    176   renderLinePrefix(OS, ViewDepth);
    177   OS.indent(getCombinedColumnWidth(getOptions()));
    178 
    179   CoverageSegmentArray Segments = Line.getLineSegments();
    180 
    181   // Just consider the segments which start *and* end on this line.
    182   if (Segments.size() > 1)
    183     Segments = Segments.drop_back();
    184 
    185   unsigned PrevColumn = 1;
    186   for (const auto *S : Segments) {
    187     if (!S->IsRegionEntry)
    188       continue;
    189     if (S->Count == Line.getExecutionCount())
    190       continue;
    191     // Skip to the new region.
    192     if (S->Col > PrevColumn)
    193       OS.indent(S->Col - PrevColumn);
    194     PrevColumn = S->Col + 1;
    195     std::string C = formatCount(S->Count);
    196     PrevColumn += C.size();
    197     OS << '^' << C;
    198 
    199     if (getOptions().Debug)
    200       errs() << "Marker at " << S->Line << ":" << S->Col << " = "
    201             << formatCount(S->Count) << "\n";
    202   }
    203   OS << '\n';
    204 }
    205 
    206 void SourceCoverageViewText::renderExpansionSite(raw_ostream &OS, LineRef L,
    207                                                  const LineCoverageStats &LCS,
    208                                                  unsigned ExpansionCol,
    209                                                  unsigned ViewDepth) {
    210   renderLinePrefix(OS, ViewDepth);
    211   OS.indent(getCombinedColumnWidth(getOptions()) + (ViewDepth == 0 ? 0 : 1));
    212   renderLine(OS, L, LCS, ExpansionCol, ViewDepth);
    213 }
    214 
    215 void SourceCoverageViewText::renderExpansionView(raw_ostream &OS,
    216                                                  ExpansionView &ESV,
    217                                                  unsigned ViewDepth) {
    218   // Render the child subview.
    219   if (getOptions().Debug)
    220     errs() << "Expansion at line " << ESV.getLine() << ", " << ESV.getStartCol()
    221            << " -> " << ESV.getEndCol() << '\n';
    222   ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
    223                   /*ShowTitle=*/false, ViewDepth + 1);
    224 }
    225 
    226 void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,
    227                                                      InstantiationView &ISV,
    228                                                      unsigned ViewDepth) {
    229   renderLinePrefix(OS, ViewDepth);
    230   OS << ' ';
    231   if (!ISV.View)
    232     getOptions().colored_ostream(OS, raw_ostream::RED)
    233         << "Unexecuted instantiation: " << ISV.FunctionName << "\n";
    234   else
    235     ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true,
    236                     /*ShowTitle=*/false, ViewDepth);
    237 }
    238 
    239 void SourceCoverageViewText::renderTitle(raw_ostream &OS, StringRef Title) {
    240   if (getOptions().hasProjectTitle())
    241     getOptions().colored_ostream(OS, raw_ostream::CYAN)
    242         << getOptions().ProjectTitle << "\n";
    243 
    244   getOptions().colored_ostream(OS, raw_ostream::CYAN) << Title << "\n";
    245 
    246   if (getOptions().hasCreatedTime())
    247     getOptions().colored_ostream(OS, raw_ostream::CYAN)
    248         << getOptions().CreatedTimeStr << "\n";
    249 }
    250 
    251 void SourceCoverageViewText::renderTableHeader(raw_ostream &, unsigned,
    252                                                unsigned) {}
    253