brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.8 KiB · df8eb1d Raw
416 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file This file implements the text-based coverage renderer.10///11//===----------------------------------------------------------------------===//12 13#include "SourceCoverageViewText.h"14#include "CoverageReport.h"15#include "llvm/ADT/SmallString.h"16#include "llvm/ADT/StringExtras.h"17#include "llvm/Support/FileSystem.h"18#include "llvm/Support/Format.h"19#include "llvm/Support/Path.h"20#include <optional>21 22using namespace llvm;23 24Expected<CoveragePrinter::OwnedStream>25CoveragePrinterText::createViewFile(StringRef Path, bool InToplevel) {26  return createOutputStream(Path, "txt", InToplevel);27}28 29void CoveragePrinterText::closeViewFile(OwnedStream OS) {30  OS->operator<<('\n');31}32 33Error CoveragePrinterText::createIndexFile(34    ArrayRef<std::string> SourceFiles, const CoverageMapping &Coverage,35    const CoverageFiltersMatchAll &Filters) {36  auto OSOrErr = createOutputStream("index", "txt", /*InToplevel=*/true);37  if (Error E = OSOrErr.takeError())38    return E;39  auto OS = std::move(OSOrErr.get());40  raw_ostream &OSRef = *OS.get();41 42  CoverageReport Report(Opts, Coverage);43  Report.renderFileReports(OSRef, SourceFiles, Filters);44 45  Opts.colored_ostream(OSRef, raw_ostream::CYAN) << "\n"46                                                 << Opts.getLLVMVersionString();47 48  return Error::success();49}50 51struct CoveragePrinterTextDirectory::Reporter : public DirectoryCoverageReport {52  CoveragePrinterTextDirectory &Printer;53 54  Reporter(CoveragePrinterTextDirectory &Printer,55           const coverage::CoverageMapping &Coverage,56           const CoverageFiltersMatchAll &Filters)57      : DirectoryCoverageReport(Printer.Opts, Coverage, Filters),58        Printer(Printer) {}59 60  Error generateSubDirectoryReport(SubFileReports &&SubFiles,61                                   SubDirReports &&SubDirs,62                                   FileCoverageSummary &&SubTotals) override {63    auto &LCPath = SubTotals.Name;64    assert(Options.hasOutputDirectory() &&65           "No output directory for index file");66 67    SmallString<128> OSPath = LCPath;68    sys::path::append(OSPath, "index");69    auto OSOrErr = Printer.createOutputStream(OSPath, "txt",70                                              /*InToplevel=*/false);71    if (auto E = OSOrErr.takeError())72      return E;73    auto OS = std::move(OSOrErr.get());74    raw_ostream &OSRef = *OS.get();75 76    std::vector<FileCoverageSummary> Reports;77    for (auto &&SubDir : SubDirs)78      Reports.push_back(std::move(SubDir.second.first));79    for (auto &&SubFile : SubFiles)80      Reports.push_back(std::move(SubFile.second));81 82    CoverageReport Report(Options, Coverage);83    Report.renderFileReports(OSRef, Reports, SubTotals, Filters.empty());84 85    Options.colored_ostream(OSRef, raw_ostream::CYAN)86        << "\n"87        << Options.getLLVMVersionString();88 89    return Error::success();90  }91};92 93Error CoveragePrinterTextDirectory::createIndexFile(94    ArrayRef<std::string> SourceFiles, const CoverageMapping &Coverage,95    const CoverageFiltersMatchAll &Filters) {96  if (SourceFiles.size() <= 1)97    return CoveragePrinterText::createIndexFile(SourceFiles, Coverage, Filters);98 99  Reporter Report(*this, Coverage, Filters);100  auto TotalsOrErr = Report.prepareDirectoryReports(SourceFiles);101  if (auto E = TotalsOrErr.takeError())102    return E;103  auto &LCPath = TotalsOrErr->Name;104 105  auto TopIndexFilePath =106      getOutputPath("index", "txt", /*InToplevel=*/true, /*Relative=*/false);107  auto LCPIndexFilePath =108      getOutputPath((LCPath + "index").str(), "txt", /*InToplevel=*/false,109                    /*Relative=*/false);110  return errorCodeToError(111      sys::fs::copy_file(LCPIndexFilePath, TopIndexFilePath));112}113 114namespace {115 116static const unsigned LineCoverageColumnWidth = 7;117static const unsigned LineNumberColumnWidth = 5;118 119/// Get the width of the leading columns.120unsigned getCombinedColumnWidth(const CoverageViewOptions &Opts) {121  return (Opts.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +122         (Opts.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);123}124 125/// The width of the line that is used to divide between the view and126/// the subviews.127unsigned getDividerWidth(const CoverageViewOptions &Opts) {128  return getCombinedColumnWidth(Opts) + 4;129}130 131} // anonymous namespace132 133void SourceCoverageViewText::renderViewHeader(raw_ostream &) {}134 135void SourceCoverageViewText::renderViewFooter(raw_ostream &) {}136 137void SourceCoverageViewText::renderSourceName(raw_ostream &OS, bool WholeFile) {138  getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName()139                                                      << ":\n";140}141 142void SourceCoverageViewText::renderLinePrefix(raw_ostream &OS,143                                              unsigned ViewDepth) {144  for (unsigned I = 0; I < ViewDepth; ++I)145    OS << "  |";146}147 148void SourceCoverageViewText::renderLineSuffix(raw_ostream &, unsigned) {}149 150void SourceCoverageViewText::renderViewDivider(raw_ostream &OS,151                                               unsigned ViewDepth) {152  assert(ViewDepth != 0 && "Cannot render divider at top level");153  renderLinePrefix(OS, ViewDepth - 1);154  OS.indent(2);155  unsigned Length = getDividerWidth(getOptions());156  for (unsigned I = 0; I < Length; ++I)157    OS << '-';158  OS << '\n';159}160 161void SourceCoverageViewText::renderLine(raw_ostream &OS, LineRef L,162                                        const LineCoverageStats &LCS,163                                        unsigned ExpansionCol,164                                        unsigned ViewDepth) {165  StringRef Line = L.Line;166  unsigned LineNumber = L.LineNo;167  auto *WrappedSegment = LCS.getWrappedSegment();168  CoverageSegmentArray Segments = LCS.getLineSegments();169 170  std::optional<raw_ostream::Colors> Highlight;171  SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;172 173  // The first segment overlaps from a previous line, so we treat it specially.174  if (WrappedSegment && !WrappedSegment->IsGapRegion &&175      WrappedSegment->HasCount && WrappedSegment->Count == 0)176    Highlight = raw_ostream::RED;177 178  // Output each segment of the line, possibly highlighted.179  unsigned Col = 1;180  for (const auto *S : Segments) {181    unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);182    colored_ostream(OS, Highlight.value_or(raw_ostream::SAVEDCOLOR),183                    getOptions().Colors && Highlight, /*Bold=*/false,184                    /*BG=*/true)185        << Line.substr(Col - 1, End - Col);186    if (getOptions().Debug && Highlight)187      HighlightedRanges.push_back(std::make_pair(Col, End));188    Col = End;189    if ((!S->IsGapRegion || Highlight == raw_ostream::RED) && S->HasCount &&190        S->Count == 0)191      Highlight = raw_ostream::RED;192    else if (Col == ExpansionCol)193      Highlight = raw_ostream::CYAN;194    else195      Highlight = std::nullopt;196  }197 198  // Show the rest of the line.199  colored_ostream(OS, Highlight.value_or(raw_ostream::SAVEDCOLOR),200                  getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true)201      << Line.substr(Col - 1, Line.size() - Col + 1);202  OS << '\n';203 204  if (getOptions().Debug) {205    for (const auto &Range : HighlightedRanges)206      errs() << "Highlighted line " << LineNumber << ", " << Range.first207             << " -> " << Range.second << '\n';208    if (Highlight)209      errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n";210  }211}212 213void SourceCoverageViewText::renderLineCoverageColumn(214    raw_ostream &OS, const LineCoverageStats &Line) {215  if (!Line.isMapped()) {216    OS.indent(LineCoverageColumnWidth) << '|';217    return;218  }219  std::string C = formatBinaryCount(Line.getExecutionCount());220  OS.indent(LineCoverageColumnWidth - C.size());221  colored_ostream(OS, raw_ostream::MAGENTA,222                  Line.hasMultipleRegions() && getOptions().Colors)223      << C;224  OS << '|';225}226 227void SourceCoverageViewText::renderLineNumberColumn(raw_ostream &OS,228                                                    unsigned LineNo) {229  SmallString<32> Buffer;230  raw_svector_ostream BufferOS(Buffer);231  BufferOS << LineNo;232  auto Str = BufferOS.str();233  // Trim and align to the right.234  Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));235  OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';236}237 238void SourceCoverageViewText::renderRegionMarkers(raw_ostream &OS,239                                                 const LineCoverageStats &Line,240                                                 unsigned ViewDepth) {241  renderLinePrefix(OS, ViewDepth);242  OS.indent(getCombinedColumnWidth(getOptions()));243 244  CoverageSegmentArray Segments = Line.getLineSegments();245 246  // Just consider the segments which start *and* end on this line.247  if (Segments.size() > 1)248    Segments = Segments.drop_back();249 250  unsigned PrevColumn = 1;251  for (const auto *S : Segments) {252    if (!S->IsRegionEntry)253      continue;254    if (S->Count == Line.getExecutionCount())255      continue;256    // Skip to the new region.257    if (S->Col > PrevColumn)258      OS.indent(S->Col - PrevColumn);259    PrevColumn = S->Col + 1;260    std::string C = formatCount(S->Count);261    PrevColumn += C.size();262    OS << '^' << C;263 264    if (getOptions().Debug)265      errs() << "Marker at " << S->Line << ":" << S->Col << " = "266             << formatBinaryCount(S->Count) << "\n";267  }268  OS << '\n';269}270 271void SourceCoverageViewText::renderExpansionSite(raw_ostream &OS, LineRef L,272                                                 const LineCoverageStats &LCS,273                                                 unsigned ExpansionCol,274                                                 unsigned ViewDepth) {275  renderLinePrefix(OS, ViewDepth);276  OS.indent(getCombinedColumnWidth(getOptions()) + (ViewDepth == 0 ? 0 : 1));277  renderLine(OS, L, LCS, ExpansionCol, ViewDepth);278}279 280void SourceCoverageViewText::renderExpansionView(raw_ostream &OS,281                                                 ExpansionView &ESV,282                                                 unsigned ViewDepth) {283  // Render the child subview.284  if (getOptions().Debug)285    errs() << "Expansion at line " << ESV.getLine() << ", " << ESV.getStartCol()286           << " -> " << ESV.getEndCol() << '\n';287  ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,288                  /*ShowTitle=*/false, ViewDepth + 1);289}290 291void SourceCoverageViewText::renderBranchView(raw_ostream &OS, BranchView &BRV,292                                              unsigned ViewDepth) {293  // Render the child subview.294  if (getOptions().Debug)295    errs() << "Branch at line " << BRV.getLine() << '\n';296 297  auto BranchCount = [&](StringRef Label, uint64_t Count, bool Folded,298                         double Total) {299    if (Folded)300      return std::string{"Folded"};301 302    std::string Str;303    raw_string_ostream OS(Str);304 305    colored_ostream(OS, raw_ostream::RED, getOptions().Colors && !Count,306                    /*Bold=*/false, /*BG=*/true)307        << Label;308 309    if (getOptions().ShowBranchCounts)310      OS << ": " << formatBinaryCount(Count);311    else312      OS << ": " << format("%0.2f", (Total != 0 ? 100.0 * Count / Total : 0.0))313         << "%";314 315    return Str;316  };317 318  for (const auto &R : BRV.Regions) {319    // This can be `double` since it is only used as a denominator.320    // FIXME: It is still inaccurate if Count is greater than (1LL << 53).321    double Total =322        static_cast<double>(R.ExecutionCount) + R.FalseExecutionCount;323 324    renderLinePrefix(OS, ViewDepth);325    OS << "  Branch (" << R.LineStart << ":" << R.ColumnStart << "): [";326 327    if (R.TrueFolded && R.FalseFolded) {328      OS << "Folded - Ignored]\n";329      continue;330    }331 332    OS << BranchCount("True", R.ExecutionCount, R.TrueFolded, Total) << ", "333       << BranchCount("False", R.FalseExecutionCount, R.FalseFolded, Total)334       << "]\n";335  }336}337 338void SourceCoverageViewText::renderMCDCView(raw_ostream &OS, MCDCView &MRV,339                                            unsigned ViewDepth) {340  for (auto &Record : MRV.Records) {341    renderLinePrefix(OS, ViewDepth);342    OS << "---> MC/DC Decision Region (";343    // Display Line + Column information.344    const CounterMappingRegion &DecisionRegion = Record.getDecisionRegion();345    OS << DecisionRegion.LineStart << ":";346    OS << DecisionRegion.ColumnStart << ") to (";347    OS << DecisionRegion.LineEnd << ":";348    OS << DecisionRegion.ColumnEnd << ")\n";349    renderLinePrefix(OS, ViewDepth);350    OS << "\n";351 352    // Display MC/DC Information.353    renderLinePrefix(OS, ViewDepth);354    OS << "  Number of Conditions: " << Record.getNumConditions() << "\n";355    for (unsigned i = 0; i < Record.getNumConditions(); i++) {356      renderLinePrefix(OS, ViewDepth);357      OS << "     " << Record.getConditionHeaderString(i);358    }359    renderLinePrefix(OS, ViewDepth);360    OS << "\n";361    renderLinePrefix(OS, ViewDepth);362    OS << "  Executed MC/DC Test Vectors:\n";363    renderLinePrefix(OS, ViewDepth);364    OS << "\n";365    renderLinePrefix(OS, ViewDepth);366    OS << "     ";367    OS << Record.getTestVectorHeaderString();368    for (unsigned i = 0; i < Record.getNumTestVectors(); i++) {369      renderLinePrefix(OS, ViewDepth);370      OS << Record.getTestVectorString(i);371    }372    renderLinePrefix(OS, ViewDepth);373    OS << "\n";374    for (unsigned i = 0; i < Record.getNumConditions(); i++) {375      renderLinePrefix(OS, ViewDepth);376      OS << Record.getConditionCoverageString(i);377    }378    renderLinePrefix(OS, ViewDepth);379    OS << "  MC/DC Coverage for Decision: ";380    colored_ostream(OS, raw_ostream::RED,381                    getOptions().Colors && Record.getPercentCovered() < 100.0,382                    /*Bold=*/false, /*BG=*/true)383        << format("%0.2f", Record.getPercentCovered()) << "%";384    OS << "\n";385    renderLinePrefix(OS, ViewDepth);386    OS << "\n";387  }388}389 390void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,391                                                     InstantiationView &ISV,392                                                     unsigned ViewDepth) {393  renderLinePrefix(OS, ViewDepth);394  OS << ' ';395  if (!ISV.View)396    getOptions().colored_ostream(OS, raw_ostream::RED)397        << "Unexecuted instantiation: " << ISV.FunctionName << "\n";398  else399    ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true,400                    /*ShowTitle=*/false, ViewDepth);401}402 403void SourceCoverageViewText::renderTitle(raw_ostream &OS, StringRef Title) {404  if (getOptions().hasProjectTitle())405    getOptions().colored_ostream(OS, raw_ostream::CYAN)406        << getOptions().ProjectTitle << "\n";407 408  getOptions().colored_ostream(OS, raw_ostream::CYAN) << Title << "\n";409 410  if (getOptions().hasCreatedTime())411    getOptions().colored_ostream(OS, raw_ostream::CYAN)412        << getOptions().CreatedTimeStr << "\n";413}414 415void SourceCoverageViewText::renderTableHeader(raw_ostream &, unsigned) {}416