brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.8 KiB · 336ed47 Raw
316 lines · cpp
1//===- SourceCoverageView.cpp - Code coverage view for source code --------===//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 class implements rendering for code coverage of source code.10///11//===----------------------------------------------------------------------===//12 13#include "SourceCoverageView.h"14#include "SourceCoverageViewHTML.h"15#include "SourceCoverageViewText.h"16#include "llvm/ADT/SmallString.h"17#include "llvm/ADT/StringExtras.h"18#include "llvm/Support/FileSystem.h"19#include "llvm/Support/LineIterator.h"20#include "llvm/Support/Path.h"21 22using namespace llvm;23 24ExpansionView::ExpansionView(const CounterMappingRegion &Region,25                             std::unique_ptr<SourceCoverageView> View)26    : Region(Region), View(std::move(View)) {}27 28ExpansionView::ExpansionView(ExpansionView &&RHS)29    : Region(std::move(RHS.Region)), View(std::move(RHS.View)) {}30 31ExpansionView &ExpansionView::operator=(ExpansionView &&RHS) {32  Region = std::move(RHS.Region);33  View = std::move(RHS.View);34  return *this;35}36 37InstantiationView::InstantiationView(StringRef FunctionName, unsigned Line,38                                     std::unique_ptr<SourceCoverageView> View)39    : FunctionName(FunctionName), Line(Line), View(std::move(View)) {}40 41void CoveragePrinter::StreamDestructor::operator()(raw_ostream *OS) const {42  if (OS == &outs())43    return;44  delete OS;45}46 47std::string CoveragePrinter::getOutputPath(StringRef Path, StringRef Extension,48                                           bool InToplevel,49                                           bool Relative) const {50  assert(!Extension.empty() && "The file extension may not be empty");51 52  SmallString<256> FullPath;53 54  if (!Relative)55    FullPath.append(Opts.ShowOutputDirectory);56 57  if (!InToplevel)58    sys::path::append(FullPath, getCoverageDir());59 60  SmallString<256> ParentPath = sys::path::parent_path(Path);61  sys::path::remove_dots(ParentPath, /*remove_dot_dot=*/true);62  sys::path::append(FullPath, sys::path::relative_path(ParentPath));63 64  auto PathFilename = (sys::path::filename(Path) + "." + Extension).str();65  sys::path::append(FullPath, PathFilename);66  sys::path::native(FullPath);67 68  return std::string(FullPath);69}70 71Expected<CoveragePrinter::OwnedStream>72CoveragePrinter::createOutputStream(StringRef Path, StringRef Extension,73                                    bool InToplevel) const {74  if (!Opts.hasOutputDirectory())75    return OwnedStream(&outs());76 77  std::string FullPath = getOutputPath(Path, Extension, InToplevel, false);78 79  auto ParentDir = sys::path::parent_path(FullPath);80  if (auto E = sys::fs::create_directories(ParentDir))81    return errorCodeToError(E);82 83  std::error_code E;84  raw_ostream *RawStream =85      new raw_fd_ostream(FullPath, E, sys::fs::FA_Read | sys::fs::FA_Write);86  auto OS = CoveragePrinter::OwnedStream(RawStream);87  if (E)88    return errorCodeToError(E);89  return std::move(OS);90}91 92std::unique_ptr<CoveragePrinter>93CoveragePrinter::create(const CoverageViewOptions &Opts) {94  switch (Opts.Format) {95  case CoverageViewOptions::OutputFormat::Text:96    if (Opts.ShowDirectoryCoverage)97      return std::make_unique<CoveragePrinterTextDirectory>(Opts);98    return std::make_unique<CoveragePrinterText>(Opts);99  case CoverageViewOptions::OutputFormat::HTML:100    if (Opts.ShowDirectoryCoverage)101      return std::make_unique<CoveragePrinterHTMLDirectory>(Opts);102    return std::make_unique<CoveragePrinterHTML>(Opts);103  case CoverageViewOptions::OutputFormat::Lcov:104    // Unreachable because CodeCoverage.cpp should terminate with an error105    // before we get here.106    llvm_unreachable("Lcov format is not supported!");107  }108  llvm_unreachable("Unknown coverage output format!");109}110 111unsigned SourceCoverageView::getFirstUncoveredLineNo() {112  const auto MinSegIt = find_if(CoverageInfo, [](const CoverageSegment &S) {113    return S.HasCount && S.Count == 0;114  });115 116  // There is no uncovered line, return zero.117  if (MinSegIt == CoverageInfo.end())118    return 0;119 120  return (*MinSegIt).Line;121}122 123std::string SourceCoverageView::formatCount(uint64_t N) {124  std::string Number = utostr(N);125  int Len = Number.size();126  if (Len <= 3)127    return Number;128  int IntLen = Len % 3 == 0 ? 3 : Len % 3;129  std::string Result(Number.data(), IntLen);130  if (IntLen != 3) {131    Result.push_back('.');132    Result += Number.substr(IntLen, 3 - IntLen);133  }134  Result.push_back(" kMGTPEZY"[(Len - 1) / 3]);135  return Result;136}137 138bool SourceCoverageView::shouldRenderRegionMarkers(139    const LineCoverageStats &LCS) const {140  if (!getOptions().ShowRegionMarkers)141    return false;142 143  CoverageSegmentArray Segments = LCS.getLineSegments();144  if (Segments.empty())145    return false;146  for (unsigned I = 0, E = Segments.size() - 1; I < E; ++I) {147    const auto *CurSeg = Segments[I];148    if (!CurSeg->IsRegionEntry || CurSeg->Count == LCS.getExecutionCount())149      continue;150    if (!CurSeg->HasCount) // don't show tooltips for SkippedRegions151      continue;152    return true;153  }154  return false;155}156 157bool SourceCoverageView::hasSubViews() const {158  return !ExpansionSubViews.empty() || !InstantiationSubViews.empty() ||159         !BranchSubViews.empty() || !MCDCSubViews.empty();160}161 162std::unique_ptr<SourceCoverageView>163SourceCoverageView::create(StringRef SourceName, const MemoryBuffer &File,164                           const CoverageViewOptions &Options,165                           CoverageData &&CoverageInfo) {166  switch (Options.Format) {167  case CoverageViewOptions::OutputFormat::Text:168    return std::make_unique<SourceCoverageViewText>(169        SourceName, File, Options, std::move(CoverageInfo));170  case CoverageViewOptions::OutputFormat::HTML:171    return std::make_unique<SourceCoverageViewHTML>(172        SourceName, File, Options, std::move(CoverageInfo));173  case CoverageViewOptions::OutputFormat::Lcov:174    // Unreachable because CodeCoverage.cpp should terminate with an error175    // before we get here.176    llvm_unreachable("Lcov format is not supported!");177  }178  llvm_unreachable("Unknown coverage output format!");179}180 181std::string SourceCoverageView::getSourceName() const {182  SmallString<128> SourceText(SourceName);183  sys::path::remove_dots(SourceText, /*remove_dot_dot=*/true);184  sys::path::native(SourceText);185  return std::string(SourceText);186}187 188void SourceCoverageView::addExpansion(189    const CounterMappingRegion &Region,190    std::unique_ptr<SourceCoverageView> View) {191  ExpansionSubViews.emplace_back(Region, std::move(View));192}193 194void SourceCoverageView::addBranch(unsigned Line,195                                   SmallVector<CountedRegion, 0> Regions) {196  BranchSubViews.emplace_back(Line, std::move(Regions));197}198 199void SourceCoverageView::addMCDCRecord(unsigned Line,200                                       SmallVector<MCDCRecord, 0> Records) {201  MCDCSubViews.emplace_back(Line, std::move(Records));202}203 204void SourceCoverageView::addInstantiation(205    StringRef FunctionName, unsigned Line,206    std::unique_ptr<SourceCoverageView> View) {207  InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View));208}209 210void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,211                               bool ShowSourceName, bool ShowTitle,212                               unsigned ViewDepth) {213  if (ShowTitle)214    renderTitle(OS, "Coverage Report");215 216  renderViewHeader(OS);217 218  if (ShowSourceName)219    renderSourceName(OS, WholeFile);220 221  renderTableHeader(OS, ViewDepth);222 223  // We need the expansions, instantiations, and branches sorted so we can go224  // through them while we iterate lines.225  llvm::stable_sort(ExpansionSubViews);226  llvm::stable_sort(InstantiationSubViews);227  llvm::stable_sort(BranchSubViews);228  llvm::stable_sort(MCDCSubViews);229  auto NextESV = ExpansionSubViews.begin();230  auto EndESV = ExpansionSubViews.end();231  auto NextISV = InstantiationSubViews.begin();232  auto EndISV = InstantiationSubViews.end();233  auto NextBRV = BranchSubViews.begin();234  auto EndBRV = BranchSubViews.end();235  auto NextMSV = MCDCSubViews.begin();236  auto EndMSV = MCDCSubViews.end();237 238  // Get the coverage information for the file.239  auto StartSegment = CoverageInfo.begin();240  auto EndSegment = CoverageInfo.end();241  LineCoverageIterator LCI{CoverageInfo, 1};242  LineCoverageIterator LCIEnd = LCI.getEnd();243 244  unsigned FirstLine = StartSegment != EndSegment ? StartSegment->Line : 0;245  for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof();246       ++LI, ++LCI) {247    // If we aren't rendering the whole file, we need to filter out the prologue248    // and epilogue.249    if (!WholeFile) {250      if (LCI == LCIEnd)251        break;252      else if (LI.line_number() < FirstLine)253        continue;254    }255 256    renderLinePrefix(OS, ViewDepth);257    if (getOptions().ShowLineNumbers)258      renderLineNumberColumn(OS, LI.line_number());259 260    if (getOptions().ShowLineStats)261      renderLineCoverageColumn(OS, *LCI);262 263    // If there are expansion subviews, we want to highlight the first one.264    unsigned ExpansionColumn = 0;265    if (NextESV != EndESV && NextESV->getLine() == LI.line_number() &&266        getOptions().Colors)267      ExpansionColumn = NextESV->getStartCol();268 269    // Display the source code for the current line.270    renderLine(OS, {*LI, LI.line_number()}, *LCI, ExpansionColumn, ViewDepth);271 272    // Show the region markers.273    if (shouldRenderRegionMarkers(*LCI))274      renderRegionMarkers(OS, *LCI, ViewDepth);275 276    // Show the expansions, instantiations, and branches for this line.277    bool RenderedSubView = false;278    for (; NextESV != EndESV && NextESV->getLine() == LI.line_number();279         ++NextESV) {280      renderViewDivider(OS, ViewDepth + 1);281 282      // Re-render the current line and highlight the expansion range for283      // this subview.284      if (RenderedSubView) {285        ExpansionColumn = NextESV->getStartCol();286        renderExpansionSite(OS, {*LI, LI.line_number()}, *LCI, ExpansionColumn,287                            ViewDepth);288        renderViewDivider(OS, ViewDepth + 1);289      }290 291      renderExpansionView(OS, *NextESV, ViewDepth + 1);292      RenderedSubView = true;293    }294    for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) {295      renderViewDivider(OS, ViewDepth + 1);296      renderInstantiationView(OS, *NextISV, ViewDepth + 1);297      RenderedSubView = true;298    }299    for (; NextBRV != EndBRV && NextBRV->Line == LI.line_number(); ++NextBRV) {300      renderViewDivider(OS, ViewDepth + 1);301      renderBranchView(OS, *NextBRV, ViewDepth + 1);302      RenderedSubView = true;303    }304    for (; NextMSV != EndMSV && NextMSV->Line == LI.line_number(); ++NextMSV) {305      renderViewDivider(OS, ViewDepth + 1);306      renderMCDCView(OS, *NextMSV, ViewDepth + 1);307      RenderedSubView = true;308    }309    if (RenderedSubView)310      renderViewDivider(OS, ViewDepth + 1);311    renderLineSuffix(OS, ViewDepth);312  }313 314  renderViewFooter(OS);315}316