Home | History | Annotate | Download | only in llvm-cov
      1 //===- CoverageReport.cpp - Code coverage report -------------------------===//
      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 // This class implements rendering of a code coverage report.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "CoverageReport.h"
     15 #include "RenderingSupport.h"
     16 #include "llvm/Support/FileSystem.h"
     17 #include "llvm/Support/Format.h"
     18 
     19 using namespace llvm;
     20 namespace {
     21 /// \brief Helper struct which prints trimmed and aligned columns.
     22 struct Column {
     23   enum TrimKind { NoTrim, WidthTrim, LeftTrim, RightTrim };
     24 
     25   enum AlignmentKind { LeftAlignment, RightAlignment };
     26 
     27   StringRef Str;
     28   unsigned Width;
     29   TrimKind Trim;
     30   AlignmentKind Alignment;
     31 
     32   Column(StringRef Str, unsigned Width)
     33       : Str(Str), Width(Width), Trim(WidthTrim), Alignment(LeftAlignment) {}
     34 
     35   Column &set(TrimKind Value) {
     36     Trim = Value;
     37     return *this;
     38   }
     39 
     40   Column &set(AlignmentKind Value) {
     41     Alignment = Value;
     42     return *this;
     43   }
     44 
     45   void render(raw_ostream &OS) const;
     46 };
     47 
     48 raw_ostream &operator<<(raw_ostream &OS, const Column &Value) {
     49   Value.render(OS);
     50   return OS;
     51 }
     52 }
     53 
     54 void Column::render(raw_ostream &OS) const {
     55   if (Str.size() <= Width) {
     56     if (Alignment == RightAlignment) {
     57       OS.indent(Width - Str.size());
     58       OS << Str;
     59       return;
     60     }
     61     OS << Str;
     62     OS.indent(Width - Str.size());
     63     return;
     64   }
     65 
     66   switch (Trim) {
     67   case NoTrim:
     68     OS << Str;
     69     break;
     70   case WidthTrim:
     71     OS << Str.substr(0, Width);
     72     break;
     73   case LeftTrim:
     74     OS << "..." << Str.substr(Str.size() - Width + 3);
     75     break;
     76   case RightTrim:
     77     OS << Str.substr(0, Width - 3) << "...";
     78     break;
     79   }
     80 }
     81 
     82 static Column column(StringRef Str, unsigned Width) {
     83   return Column(Str, Width);
     84 }
     85 
     86 template <typename T>
     87 static Column column(StringRef Str, unsigned Width, const T &Value) {
     88   return Column(Str, Width).set(Value);
     89 }
     90 
     91 static size_t FileReportColumns[] = {25, 10, 8, 8, 10, 10};
     92 static size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8};
     93 
     94 /// \brief  Adjust column widths to fit long file paths and function names.
     95 static void adjustColumnWidths(coverage::CoverageMapping *CM) {
     96   for (StringRef Filename : CM->getUniqueSourceFiles()) {
     97     FileReportColumns[0] = std::max(FileReportColumns[0], Filename.size());
     98     for (const auto &F : CM->getCoveredFunctions(Filename)) {
     99       FunctionReportColumns[0] =
    100           std::max(FunctionReportColumns[0], F.Name.size());
    101     }
    102   }
    103 }
    104 
    105 /// \brief Prints a horizontal divider which spans across the given columns.
    106 template <typename T, size_t N>
    107 static void renderDivider(T (&Columns)[N], raw_ostream &OS) {
    108   unsigned Length = 0;
    109   for (unsigned I = 0; I < N; ++I)
    110     Length += Columns[I];
    111   for (unsigned I = 0; I < Length; ++I)
    112     OS << '-';
    113 }
    114 
    115 /// \brief Return the color which correponds to the coverage
    116 /// percentage of a certain metric.
    117 template <typename T>
    118 static raw_ostream::Colors determineCoveragePercentageColor(const T &Info) {
    119   if (Info.isFullyCovered())
    120     return raw_ostream::GREEN;
    121   return Info.getPercentCovered() >= 80.0 ? raw_ostream::YELLOW
    122                                           : raw_ostream::RED;
    123 }
    124 
    125 void CoverageReport::render(const FileCoverageSummary &File, raw_ostream &OS) {
    126   OS << column(File.Name, FileReportColumns[0], Column::NoTrim)
    127      << format("%*u", FileReportColumns[1],
    128                (unsigned)File.RegionCoverage.NumRegions);
    129   Options.colored_ostream(OS, File.RegionCoverage.isFullyCovered()
    130                                   ? raw_ostream::GREEN
    131                                   : raw_ostream::RED)
    132       << format("%*u", FileReportColumns[2], (unsigned)File.RegionCoverage.NotCovered);
    133   Options.colored_ostream(OS,
    134                           determineCoveragePercentageColor(File.RegionCoverage))
    135       << format("%*.2f", FileReportColumns[3] - 1,
    136                 File.RegionCoverage.getPercentCovered()) << '%';
    137   OS << format("%*u", FileReportColumns[4],
    138                (unsigned)File.FunctionCoverage.NumFunctions);
    139   Options.colored_ostream(
    140       OS, determineCoveragePercentageColor(File.FunctionCoverage))
    141       << format("%*.2f", FileReportColumns[5] - 1,
    142                 File.FunctionCoverage.getPercentCovered()) << '%';
    143   OS << "\n";
    144 }
    145 
    146 void CoverageReport::render(const FunctionCoverageSummary &Function,
    147                             raw_ostream &OS) {
    148   OS << column(Function.Name, FunctionReportColumns[0], Column::RightTrim)
    149      << format("%*u", FunctionReportColumns[1],
    150                (unsigned)Function.RegionCoverage.NumRegions);
    151   Options.colored_ostream(OS, Function.RegionCoverage.isFullyCovered()
    152                                   ? raw_ostream::GREEN
    153                                   : raw_ostream::RED)
    154       << format("%*u", FunctionReportColumns[2],
    155                 (unsigned)Function.RegionCoverage.NotCovered);
    156   Options.colored_ostream(
    157       OS, determineCoveragePercentageColor(Function.RegionCoverage))
    158       << format("%*.2f", FunctionReportColumns[3] - 1,
    159                 Function.RegionCoverage.getPercentCovered()) << '%';
    160   OS << format("%*u", FunctionReportColumns[4],
    161                (unsigned)Function.LineCoverage.NumLines);
    162   Options.colored_ostream(OS, Function.LineCoverage.isFullyCovered()
    163                                   ? raw_ostream::GREEN
    164                                   : raw_ostream::RED)
    165       << format("%*u", FunctionReportColumns[5],
    166                 (unsigned)Function.LineCoverage.NotCovered);
    167   Options.colored_ostream(
    168       OS, determineCoveragePercentageColor(Function.LineCoverage))
    169       << format("%*.2f", FunctionReportColumns[6] - 1,
    170                 Function.LineCoverage.getPercentCovered()) << '%';
    171   OS << "\n";
    172 }
    173 
    174 void CoverageReport::renderFunctionReports(ArrayRef<StringRef> Files,
    175                                            raw_ostream &OS) {
    176   adjustColumnWidths(Coverage.get());
    177   bool isFirst = true;
    178   for (StringRef Filename : Files) {
    179     if (isFirst)
    180       isFirst = false;
    181     else
    182       OS << "\n";
    183     OS << "File '" << Filename << "':\n";
    184     OS << column("Name", FunctionReportColumns[0])
    185        << column("Regions", FunctionReportColumns[1], Column::RightAlignment)
    186        << column("Miss", FunctionReportColumns[2], Column::RightAlignment)
    187        << column("Cover", FunctionReportColumns[3], Column::RightAlignment)
    188        << column("Lines", FunctionReportColumns[4], Column::RightAlignment)
    189        << column("Miss", FunctionReportColumns[5], Column::RightAlignment)
    190        << column("Cover", FunctionReportColumns[6], Column::RightAlignment);
    191     OS << "\n";
    192     renderDivider(FunctionReportColumns, OS);
    193     OS << "\n";
    194     FunctionCoverageSummary Totals("TOTAL");
    195     for (const auto &F : Coverage->getCoveredFunctions(Filename)) {
    196       FunctionCoverageSummary Function = FunctionCoverageSummary::get(F);
    197       ++Totals.ExecutionCount;
    198       Totals.RegionCoverage += Function.RegionCoverage;
    199       Totals.LineCoverage += Function.LineCoverage;
    200       render(Function, OS);
    201     }
    202     if (Totals.ExecutionCount) {
    203       renderDivider(FunctionReportColumns, OS);
    204       OS << "\n";
    205       render(Totals, OS);
    206     }
    207   }
    208 }
    209 
    210 void CoverageReport::renderFileReports(raw_ostream &OS) {
    211   adjustColumnWidths(Coverage.get());
    212   OS << column("Filename", FileReportColumns[0])
    213      << column("Regions", FileReportColumns[1], Column::RightAlignment)
    214      << column("Miss", FileReportColumns[2], Column::RightAlignment)
    215      << column("Cover", FileReportColumns[3], Column::RightAlignment)
    216      << column("Functions", FileReportColumns[4], Column::RightAlignment)
    217      << column("Executed", FileReportColumns[5], Column::RightAlignment)
    218      << "\n";
    219   renderDivider(FileReportColumns, OS);
    220   OS << "\n";
    221 
    222   FileCoverageSummary Totals("TOTAL");
    223   for (StringRef Filename : Coverage->getUniqueSourceFiles()) {
    224     FileCoverageSummary Summary(Filename);
    225     for (const auto &F : Coverage->getCoveredFunctions(Filename)) {
    226       FunctionCoverageSummary Function = FunctionCoverageSummary::get(F);
    227       Summary.addFunction(Function);
    228       Totals.addFunction(Function);
    229     }
    230     render(Summary, OS);
    231   }
    232   renderDivider(FileReportColumns, OS);
    233   OS << "\n";
    234   render(Totals, OS);
    235 }
    236