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