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