xref: /freebsd-src/contrib/llvm-project/llvm/tools/llvm-cov/CoverageReport.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===- CoverageReport.cpp - Code coverage report -------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This class implements rendering of a code coverage report.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "CoverageReport.h"
140b57cec5SDimitry Andric #include "RenderingSupport.h"
1506c3fb27SDimitry Andric #include "llvm/ADT/SmallString.h"
160b57cec5SDimitry Andric #include "llvm/Support/Format.h"
170b57cec5SDimitry Andric #include "llvm/Support/Path.h"
180b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h"
190b57cec5SDimitry Andric #include "llvm/Support/Threading.h"
200b57cec5SDimitry Andric #include <numeric>
210b57cec5SDimitry Andric 
220b57cec5SDimitry Andric using namespace llvm;
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric namespace {
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric /// Helper struct which prints trimmed and aligned columns.
270b57cec5SDimitry Andric struct Column {
280b57cec5SDimitry Andric   enum TrimKind { NoTrim, WidthTrim, RightTrim };
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric   enum AlignmentKind { LeftAlignment, RightAlignment };
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric   StringRef Str;
330b57cec5SDimitry Andric   unsigned Width;
340b57cec5SDimitry Andric   TrimKind Trim;
350b57cec5SDimitry Andric   AlignmentKind Alignment;
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric   Column(StringRef Str, unsigned Width)
380b57cec5SDimitry Andric       : Str(Str), Width(Width), Trim(WidthTrim), Alignment(LeftAlignment) {}
390b57cec5SDimitry Andric 
400b57cec5SDimitry Andric   Column &set(TrimKind Value) {
410b57cec5SDimitry Andric     Trim = Value;
420b57cec5SDimitry Andric     return *this;
430b57cec5SDimitry Andric   }
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric   Column &set(AlignmentKind Value) {
460b57cec5SDimitry Andric     Alignment = Value;
470b57cec5SDimitry Andric     return *this;
480b57cec5SDimitry Andric   }
490b57cec5SDimitry Andric 
500b57cec5SDimitry Andric   void render(raw_ostream &OS) const {
510b57cec5SDimitry Andric     if (Str.size() <= Width) {
520b57cec5SDimitry Andric       if (Alignment == RightAlignment) {
530b57cec5SDimitry Andric         OS.indent(Width - Str.size());
540b57cec5SDimitry Andric         OS << Str;
550b57cec5SDimitry Andric         return;
560b57cec5SDimitry Andric       }
570b57cec5SDimitry Andric       OS << Str;
580b57cec5SDimitry Andric       OS.indent(Width - Str.size());
590b57cec5SDimitry Andric       return;
600b57cec5SDimitry Andric     }
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric     switch (Trim) {
630b57cec5SDimitry Andric     case NoTrim:
640b57cec5SDimitry Andric       OS << Str;
650b57cec5SDimitry Andric       break;
660b57cec5SDimitry Andric     case WidthTrim:
670b57cec5SDimitry Andric       OS << Str.substr(0, Width);
680b57cec5SDimitry Andric       break;
690b57cec5SDimitry Andric     case RightTrim:
700b57cec5SDimitry Andric       OS << Str.substr(0, Width - 3) << "...";
710b57cec5SDimitry Andric       break;
720b57cec5SDimitry Andric     }
730b57cec5SDimitry Andric   }
740b57cec5SDimitry Andric };
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric raw_ostream &operator<<(raw_ostream &OS, const Column &Value) {
770b57cec5SDimitry Andric   Value.render(OS);
780b57cec5SDimitry Andric   return OS;
790b57cec5SDimitry Andric }
800b57cec5SDimitry Andric 
810b57cec5SDimitry Andric Column column(StringRef Str, unsigned Width) { return Column(Str, Width); }
820b57cec5SDimitry Andric 
830b57cec5SDimitry Andric template <typename T>
840b57cec5SDimitry Andric Column column(StringRef Str, unsigned Width, const T &Value) {
850b57cec5SDimitry Andric   return Column(Str, Width).set(Value);
860b57cec5SDimitry Andric }
870b57cec5SDimitry Andric 
880b57cec5SDimitry Andric // Specify the default column widths.
895f757f3fSDimitry Andric size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10, 16, 16, 10,
905f757f3fSDimitry Andric                               12, 18, 10, 12, 18, 10, 20, 21, 10};
915f757f3fSDimitry Andric size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8, 10, 8, 8, 20, 8, 8};
920b57cec5SDimitry Andric 
930b57cec5SDimitry Andric /// Adjust column widths to fit long file paths and function names.
940b57cec5SDimitry Andric void adjustColumnWidths(ArrayRef<StringRef> Files,
950b57cec5SDimitry Andric                         ArrayRef<StringRef> Functions) {
960b57cec5SDimitry Andric   for (StringRef Filename : Files)
970b57cec5SDimitry Andric     FileReportColumns[0] = std::max(FileReportColumns[0], Filename.size());
980b57cec5SDimitry Andric   for (StringRef Funcname : Functions)
990b57cec5SDimitry Andric     FunctionReportColumns[0] =
1000b57cec5SDimitry Andric         std::max(FunctionReportColumns[0], Funcname.size());
1010b57cec5SDimitry Andric }
1020b57cec5SDimitry Andric 
1030b57cec5SDimitry Andric /// Prints a horizontal divider long enough to cover the given column
1040b57cec5SDimitry Andric /// widths.
105*0fca6ea1SDimitry Andric void renderDivider(raw_ostream &OS, const CoverageViewOptions &Options, bool isFileReport) {
106*0fca6ea1SDimitry Andric   size_t Length;
107*0fca6ea1SDimitry Andric   if (isFileReport) {
108*0fca6ea1SDimitry Andric     Length = std::accumulate(std::begin(FileReportColumns), std::end(FileReportColumns), 0);
109*0fca6ea1SDimitry Andric     if (!Options.ShowRegionSummary)
110*0fca6ea1SDimitry Andric       Length -= (FileReportColumns[1] + FileReportColumns[2] + FileReportColumns[3]);
111*0fca6ea1SDimitry Andric     if (!Options.ShowInstantiationSummary)
112*0fca6ea1SDimitry Andric       Length -= (FileReportColumns[7] + FileReportColumns[8] + FileReportColumns[9]);
113*0fca6ea1SDimitry Andric     if (!Options.ShowBranchSummary)
114*0fca6ea1SDimitry Andric       Length -= (FileReportColumns[13] + FileReportColumns[14] + FileReportColumns[15]);
115*0fca6ea1SDimitry Andric     if (!Options.ShowMCDCSummary)
116*0fca6ea1SDimitry Andric       Length -= (FileReportColumns[16] + FileReportColumns[17] + FileReportColumns[18]);
117*0fca6ea1SDimitry Andric   } else {
118*0fca6ea1SDimitry Andric     Length = std::accumulate(std::begin(FunctionReportColumns), std::end(FunctionReportColumns), 0);
119*0fca6ea1SDimitry Andric     if (!Options.ShowBranchSummary)
120*0fca6ea1SDimitry Andric       Length -= (FunctionReportColumns[7] + FunctionReportColumns[8] + FunctionReportColumns[9]);
121*0fca6ea1SDimitry Andric     if (!Options.ShowMCDCSummary)
122*0fca6ea1SDimitry Andric       Length -= (FunctionReportColumns[10] + FunctionReportColumns[11] + FunctionReportColumns[12]);
123*0fca6ea1SDimitry Andric   }
1240b57cec5SDimitry Andric   for (size_t I = 0; I < Length; ++I)
1250b57cec5SDimitry Andric     OS << '-';
1260b57cec5SDimitry Andric }
1270b57cec5SDimitry Andric 
1280b57cec5SDimitry Andric /// Return the color which correponds to the coverage percentage of a
1290b57cec5SDimitry Andric /// certain metric.
1300b57cec5SDimitry Andric template <typename T>
1310b57cec5SDimitry Andric raw_ostream::Colors determineCoveragePercentageColor(const T &Info) {
1320b57cec5SDimitry Andric   if (Info.isFullyCovered())
1330b57cec5SDimitry Andric     return raw_ostream::GREEN;
1340b57cec5SDimitry Andric   return Info.getPercentCovered() >= 80.0 ? raw_ostream::YELLOW
1350b57cec5SDimitry Andric                                           : raw_ostream::RED;
1360b57cec5SDimitry Andric }
1370b57cec5SDimitry Andric 
1380b57cec5SDimitry Andric /// Get the number of redundant path components in each path in \p Paths.
1390b57cec5SDimitry Andric unsigned getNumRedundantPathComponents(ArrayRef<std::string> Paths) {
1400b57cec5SDimitry Andric   // To start, set the number of redundant path components to the maximum
1410b57cec5SDimitry Andric   // possible value.
1420b57cec5SDimitry Andric   SmallVector<StringRef, 8> FirstPathComponents{sys::path::begin(Paths[0]),
1430b57cec5SDimitry Andric                                                 sys::path::end(Paths[0])};
1440b57cec5SDimitry Andric   unsigned NumRedundant = FirstPathComponents.size();
1450b57cec5SDimitry Andric 
1460b57cec5SDimitry Andric   for (unsigned I = 1, E = Paths.size(); NumRedundant > 0 && I < E; ++I) {
1470b57cec5SDimitry Andric     StringRef Path = Paths[I];
1480b57cec5SDimitry Andric     for (const auto &Component :
1490b57cec5SDimitry Andric          enumerate(make_range(sys::path::begin(Path), sys::path::end(Path)))) {
1500b57cec5SDimitry Andric       // Do not increase the number of redundant components: that would remove
1510b57cec5SDimitry Andric       // useful parts of already-visited paths.
1520b57cec5SDimitry Andric       if (Component.index() >= NumRedundant)
1530b57cec5SDimitry Andric         break;
1540b57cec5SDimitry Andric 
1550b57cec5SDimitry Andric       // Lower the number of redundant components when there's a mismatch
1560b57cec5SDimitry Andric       // between the first path, and the path under consideration.
1570b57cec5SDimitry Andric       if (FirstPathComponents[Component.index()] != Component.value()) {
1580b57cec5SDimitry Andric         NumRedundant = Component.index();
1590b57cec5SDimitry Andric         break;
1600b57cec5SDimitry Andric       }
1610b57cec5SDimitry Andric     }
1620b57cec5SDimitry Andric   }
1630b57cec5SDimitry Andric 
1640b57cec5SDimitry Andric   return NumRedundant;
1650b57cec5SDimitry Andric }
1660b57cec5SDimitry Andric 
1670b57cec5SDimitry Andric /// Determine the length of the longest redundant prefix of the paths in
1680b57cec5SDimitry Andric /// \p Paths.
1690b57cec5SDimitry Andric unsigned getRedundantPrefixLen(ArrayRef<std::string> Paths) {
1700b57cec5SDimitry Andric   // If there's at most one path, no path components are redundant.
1710b57cec5SDimitry Andric   if (Paths.size() <= 1)
1720b57cec5SDimitry Andric     return 0;
1730b57cec5SDimitry Andric 
1740b57cec5SDimitry Andric   unsigned PrefixLen = 0;
1750b57cec5SDimitry Andric   unsigned NumRedundant = getNumRedundantPathComponents(Paths);
1760b57cec5SDimitry Andric   auto Component = sys::path::begin(Paths[0]);
1770b57cec5SDimitry Andric   for (unsigned I = 0; I < NumRedundant; ++I) {
1780b57cec5SDimitry Andric     auto LastComponent = Component;
1790b57cec5SDimitry Andric     ++Component;
1800b57cec5SDimitry Andric     PrefixLen += Component - LastComponent;
1810b57cec5SDimitry Andric   }
1820b57cec5SDimitry Andric   return PrefixLen;
1830b57cec5SDimitry Andric }
1840b57cec5SDimitry Andric 
1855f757f3fSDimitry Andric /// Determine the length of the longest redundant prefix of the substrs starts
1865f757f3fSDimitry Andric /// from \p LCP in \p Paths. \p Paths can't be empty. If there's only one
1875f757f3fSDimitry Andric /// element in \p Paths, the length of the substr is returned. Note this is
1885f757f3fSDimitry Andric /// differnet from the behavior of the function above.
1895f757f3fSDimitry Andric unsigned getRedundantPrefixLen(ArrayRef<StringRef> Paths, unsigned LCP) {
1905f757f3fSDimitry Andric   assert(!Paths.empty() && "Paths must have at least one element");
1915f757f3fSDimitry Andric 
1925f757f3fSDimitry Andric   auto Iter = Paths.begin();
1935f757f3fSDimitry Andric   auto IterE = Paths.end();
1945f757f3fSDimitry Andric   auto Prefix = Iter->substr(LCP);
1955f757f3fSDimitry Andric   while (++Iter != IterE) {
1965f757f3fSDimitry Andric     auto Other = Iter->substr(LCP);
1975f757f3fSDimitry Andric     auto Len = std::min(Prefix.size(), Other.size());
1985f757f3fSDimitry Andric     for (std::size_t I = 0; I < Len; ++I) {
1995f757f3fSDimitry Andric       if (Prefix[I] != Other[I]) {
2005f757f3fSDimitry Andric         Prefix = Prefix.substr(0, I);
2015f757f3fSDimitry Andric         break;
2025f757f3fSDimitry Andric       }
2035f757f3fSDimitry Andric     }
2045f757f3fSDimitry Andric   }
2055f757f3fSDimitry Andric 
2065f757f3fSDimitry Andric   for (auto I = Prefix.size(); --I != SIZE_MAX;) {
2075f757f3fSDimitry Andric     if (Prefix[I] == '/' || Prefix[I] == '\\')
2085f757f3fSDimitry Andric       return I + 1;
2095f757f3fSDimitry Andric   }
2105f757f3fSDimitry Andric 
2115f757f3fSDimitry Andric   return Prefix.size();
2125f757f3fSDimitry Andric }
2135f757f3fSDimitry Andric 
2140b57cec5SDimitry Andric } // end anonymous namespace
2150b57cec5SDimitry Andric 
2160b57cec5SDimitry Andric namespace llvm {
2170b57cec5SDimitry Andric 
2180b57cec5SDimitry Andric void CoverageReport::render(const FileCoverageSummary &File,
2190b57cec5SDimitry Andric                             raw_ostream &OS) const {
2200b57cec5SDimitry Andric   auto FileCoverageColor =
2210b57cec5SDimitry Andric       determineCoveragePercentageColor(File.RegionCoverage);
2220b57cec5SDimitry Andric   auto FuncCoverageColor =
2230b57cec5SDimitry Andric       determineCoveragePercentageColor(File.FunctionCoverage);
2240b57cec5SDimitry Andric   auto InstantiationCoverageColor =
2250b57cec5SDimitry Andric       determineCoveragePercentageColor(File.InstantiationCoverage);
2260b57cec5SDimitry Andric   auto LineCoverageColor = determineCoveragePercentageColor(File.LineCoverage);
2270b57cec5SDimitry Andric   SmallString<256> FileName = File.Name;
2280b57cec5SDimitry Andric   sys::path::native(FileName);
2295f757f3fSDimitry Andric 
2305f757f3fSDimitry Andric   // remove_dots will remove trailing slash, so we need to check before it.
2315f757f3fSDimitry Andric   auto IsDir = FileName.ends_with(sys::path::get_separator());
2325f757f3fSDimitry Andric   sys::path::remove_dots(FileName, /*remove_dot_dot=*/true);
2335f757f3fSDimitry Andric   if (IsDir)
2345f757f3fSDimitry Andric     FileName += sys::path::get_separator();
2355f757f3fSDimitry Andric 
2360b57cec5SDimitry Andric   OS << column(FileName, FileReportColumns[0], Column::NoTrim);
2370b57cec5SDimitry Andric 
2380b57cec5SDimitry Andric   if (Options.ShowRegionSummary) {
2390b57cec5SDimitry Andric     OS << format("%*u", FileReportColumns[1],
2400b57cec5SDimitry Andric                  (unsigned)File.RegionCoverage.getNumRegions());
2410b57cec5SDimitry Andric     Options.colored_ostream(OS, FileCoverageColor)
2420b57cec5SDimitry Andric         << format("%*u", FileReportColumns[2],
2430b57cec5SDimitry Andric                   (unsigned)(File.RegionCoverage.getNumRegions() -
2440b57cec5SDimitry Andric                              File.RegionCoverage.getCovered()));
2450b57cec5SDimitry Andric     if (File.RegionCoverage.getNumRegions())
2460b57cec5SDimitry Andric       Options.colored_ostream(OS, FileCoverageColor)
2470b57cec5SDimitry Andric           << format("%*.2f", FileReportColumns[3] - 1,
2480b57cec5SDimitry Andric                     File.RegionCoverage.getPercentCovered())
2490b57cec5SDimitry Andric           << '%';
2500b57cec5SDimitry Andric     else
2510b57cec5SDimitry Andric       OS << column("-", FileReportColumns[3], Column::RightAlignment);
2520b57cec5SDimitry Andric   }
2530b57cec5SDimitry Andric 
2540b57cec5SDimitry Andric   OS << format("%*u", FileReportColumns[4],
2550b57cec5SDimitry Andric                (unsigned)File.FunctionCoverage.getNumFunctions());
2560b57cec5SDimitry Andric   OS << format("%*u", FileReportColumns[5],
2570b57cec5SDimitry Andric                (unsigned)(File.FunctionCoverage.getNumFunctions() -
2580b57cec5SDimitry Andric                           File.FunctionCoverage.getExecuted()));
2590b57cec5SDimitry Andric   if (File.FunctionCoverage.getNumFunctions())
2600b57cec5SDimitry Andric     Options.colored_ostream(OS, FuncCoverageColor)
2610b57cec5SDimitry Andric         << format("%*.2f", FileReportColumns[6] - 1,
2620b57cec5SDimitry Andric                   File.FunctionCoverage.getPercentCovered())
2630b57cec5SDimitry Andric         << '%';
2640b57cec5SDimitry Andric   else
2650b57cec5SDimitry Andric     OS << column("-", FileReportColumns[6], Column::RightAlignment);
2660b57cec5SDimitry Andric 
2670b57cec5SDimitry Andric   if (Options.ShowInstantiationSummary) {
2680b57cec5SDimitry Andric     OS << format("%*u", FileReportColumns[7],
2690b57cec5SDimitry Andric                  (unsigned)File.InstantiationCoverage.getNumFunctions());
2700b57cec5SDimitry Andric     OS << format("%*u", FileReportColumns[8],
2710b57cec5SDimitry Andric                  (unsigned)(File.InstantiationCoverage.getNumFunctions() -
2720b57cec5SDimitry Andric                             File.InstantiationCoverage.getExecuted()));
2730b57cec5SDimitry Andric     if (File.InstantiationCoverage.getNumFunctions())
2740b57cec5SDimitry Andric       Options.colored_ostream(OS, InstantiationCoverageColor)
2750b57cec5SDimitry Andric           << format("%*.2f", FileReportColumns[9] - 1,
2760b57cec5SDimitry Andric                     File.InstantiationCoverage.getPercentCovered())
2770b57cec5SDimitry Andric           << '%';
2780b57cec5SDimitry Andric     else
2790b57cec5SDimitry Andric       OS << column("-", FileReportColumns[9], Column::RightAlignment);
2800b57cec5SDimitry Andric   }
2810b57cec5SDimitry Andric 
2820b57cec5SDimitry Andric   OS << format("%*u", FileReportColumns[10],
2830b57cec5SDimitry Andric                (unsigned)File.LineCoverage.getNumLines());
2840b57cec5SDimitry Andric   Options.colored_ostream(OS, LineCoverageColor) << format(
2850b57cec5SDimitry Andric       "%*u", FileReportColumns[11], (unsigned)(File.LineCoverage.getNumLines() -
2860b57cec5SDimitry Andric                                                File.LineCoverage.getCovered()));
2870b57cec5SDimitry Andric   if (File.LineCoverage.getNumLines())
2880b57cec5SDimitry Andric     Options.colored_ostream(OS, LineCoverageColor)
2890b57cec5SDimitry Andric         << format("%*.2f", FileReportColumns[12] - 1,
2900b57cec5SDimitry Andric                   File.LineCoverage.getPercentCovered())
2910b57cec5SDimitry Andric         << '%';
2920b57cec5SDimitry Andric   else
2930b57cec5SDimitry Andric     OS << column("-", FileReportColumns[12], Column::RightAlignment);
294e8d8bef9SDimitry Andric 
295e8d8bef9SDimitry Andric   if (Options.ShowBranchSummary) {
296e8d8bef9SDimitry Andric     OS << format("%*u", FileReportColumns[13],
297e8d8bef9SDimitry Andric                  (unsigned)File.BranchCoverage.getNumBranches());
298e8d8bef9SDimitry Andric     Options.colored_ostream(OS, LineCoverageColor)
299e8d8bef9SDimitry Andric         << format("%*u", FileReportColumns[14],
300e8d8bef9SDimitry Andric                   (unsigned)(File.BranchCoverage.getNumBranches() -
301e8d8bef9SDimitry Andric                              File.BranchCoverage.getCovered()));
302e8d8bef9SDimitry Andric     if (File.BranchCoverage.getNumBranches())
303e8d8bef9SDimitry Andric       Options.colored_ostream(OS, LineCoverageColor)
304e8d8bef9SDimitry Andric           << format("%*.2f", FileReportColumns[15] - 1,
305e8d8bef9SDimitry Andric                     File.BranchCoverage.getPercentCovered())
306e8d8bef9SDimitry Andric           << '%';
307e8d8bef9SDimitry Andric     else
308e8d8bef9SDimitry Andric       OS << column("-", FileReportColumns[15], Column::RightAlignment);
309e8d8bef9SDimitry Andric   }
310e8d8bef9SDimitry Andric 
3115f757f3fSDimitry Andric   if (Options.ShowMCDCSummary) {
3125f757f3fSDimitry Andric     OS << format("%*u", FileReportColumns[16],
3135f757f3fSDimitry Andric                  (unsigned)File.MCDCCoverage.getNumPairs());
3145f757f3fSDimitry Andric     Options.colored_ostream(OS, LineCoverageColor)
3155f757f3fSDimitry Andric         << format("%*u", FileReportColumns[17],
3165f757f3fSDimitry Andric                   (unsigned)(File.MCDCCoverage.getNumPairs() -
3175f757f3fSDimitry Andric                              File.MCDCCoverage.getCoveredPairs()));
3185f757f3fSDimitry Andric     if (File.MCDCCoverage.getNumPairs())
3195f757f3fSDimitry Andric       Options.colored_ostream(OS, LineCoverageColor)
3205f757f3fSDimitry Andric           << format("%*.2f", FileReportColumns[18] - 1,
3215f757f3fSDimitry Andric                     File.MCDCCoverage.getPercentCovered())
3225f757f3fSDimitry Andric           << '%';
3235f757f3fSDimitry Andric     else
3245f757f3fSDimitry Andric       OS << column("-", FileReportColumns[18], Column::RightAlignment);
3255f757f3fSDimitry Andric   }
3265f757f3fSDimitry Andric 
3270b57cec5SDimitry Andric   OS << "\n";
3280b57cec5SDimitry Andric }
3290b57cec5SDimitry Andric 
3300b57cec5SDimitry Andric void CoverageReport::render(const FunctionCoverageSummary &Function,
3310b57cec5SDimitry Andric                             const DemangleCache &DC,
3320b57cec5SDimitry Andric                             raw_ostream &OS) const {
3330b57cec5SDimitry Andric   auto FuncCoverageColor =
3340b57cec5SDimitry Andric       determineCoveragePercentageColor(Function.RegionCoverage);
3350b57cec5SDimitry Andric   auto LineCoverageColor =
3360b57cec5SDimitry Andric       determineCoveragePercentageColor(Function.LineCoverage);
3370b57cec5SDimitry Andric   OS << column(DC.demangle(Function.Name), FunctionReportColumns[0],
3380b57cec5SDimitry Andric                Column::RightTrim)
3390b57cec5SDimitry Andric      << format("%*u", FunctionReportColumns[1],
3400b57cec5SDimitry Andric                (unsigned)Function.RegionCoverage.getNumRegions());
3410b57cec5SDimitry Andric   Options.colored_ostream(OS, FuncCoverageColor)
3420b57cec5SDimitry Andric       << format("%*u", FunctionReportColumns[2],
3430b57cec5SDimitry Andric                 (unsigned)(Function.RegionCoverage.getNumRegions() -
3440b57cec5SDimitry Andric                            Function.RegionCoverage.getCovered()));
3450b57cec5SDimitry Andric   Options.colored_ostream(
3460b57cec5SDimitry Andric       OS, determineCoveragePercentageColor(Function.RegionCoverage))
3470b57cec5SDimitry Andric       << format("%*.2f", FunctionReportColumns[3] - 1,
3480b57cec5SDimitry Andric                 Function.RegionCoverage.getPercentCovered())
3490b57cec5SDimitry Andric       << '%';
3500b57cec5SDimitry Andric   OS << format("%*u", FunctionReportColumns[4],
3510b57cec5SDimitry Andric                (unsigned)Function.LineCoverage.getNumLines());
3520b57cec5SDimitry Andric   Options.colored_ostream(OS, LineCoverageColor)
3530b57cec5SDimitry Andric       << format("%*u", FunctionReportColumns[5],
3540b57cec5SDimitry Andric                 (unsigned)(Function.LineCoverage.getNumLines() -
3550b57cec5SDimitry Andric                            Function.LineCoverage.getCovered()));
3560b57cec5SDimitry Andric   Options.colored_ostream(
3570b57cec5SDimitry Andric       OS, determineCoveragePercentageColor(Function.LineCoverage))
3580b57cec5SDimitry Andric       << format("%*.2f", FunctionReportColumns[6] - 1,
3590b57cec5SDimitry Andric                 Function.LineCoverage.getPercentCovered())
3600b57cec5SDimitry Andric       << '%';
361e8d8bef9SDimitry Andric   if (Options.ShowBranchSummary) {
362e8d8bef9SDimitry Andric     OS << format("%*u", FunctionReportColumns[7],
363e8d8bef9SDimitry Andric                  (unsigned)Function.BranchCoverage.getNumBranches());
364e8d8bef9SDimitry Andric     Options.colored_ostream(OS, LineCoverageColor)
365e8d8bef9SDimitry Andric         << format("%*u", FunctionReportColumns[8],
366e8d8bef9SDimitry Andric                   (unsigned)(Function.BranchCoverage.getNumBranches() -
367e8d8bef9SDimitry Andric                              Function.BranchCoverage.getCovered()));
368e8d8bef9SDimitry Andric     Options.colored_ostream(
369e8d8bef9SDimitry Andric         OS, determineCoveragePercentageColor(Function.BranchCoverage))
370e8d8bef9SDimitry Andric         << format("%*.2f", FunctionReportColumns[9] - 1,
371e8d8bef9SDimitry Andric                   Function.BranchCoverage.getPercentCovered())
372e8d8bef9SDimitry Andric         << '%';
373e8d8bef9SDimitry Andric   }
3745f757f3fSDimitry Andric   if (Options.ShowMCDCSummary) {
3755f757f3fSDimitry Andric     OS << format("%*u", FunctionReportColumns[10],
3765f757f3fSDimitry Andric                  (unsigned)Function.MCDCCoverage.getNumPairs());
3775f757f3fSDimitry Andric     Options.colored_ostream(OS, LineCoverageColor)
3785f757f3fSDimitry Andric         << format("%*u", FunctionReportColumns[11],
3795f757f3fSDimitry Andric                   (unsigned)(Function.MCDCCoverage.getNumPairs() -
3805f757f3fSDimitry Andric                              Function.MCDCCoverage.getCoveredPairs()));
3815f757f3fSDimitry Andric     Options.colored_ostream(
3825f757f3fSDimitry Andric         OS, determineCoveragePercentageColor(Function.MCDCCoverage))
3835f757f3fSDimitry Andric         << format("%*.2f", FunctionReportColumns[12] - 1,
3845f757f3fSDimitry Andric                   Function.MCDCCoverage.getPercentCovered())
3855f757f3fSDimitry Andric         << '%';
3865f757f3fSDimitry Andric   }
3870b57cec5SDimitry Andric   OS << "\n";
3880b57cec5SDimitry Andric }
3890b57cec5SDimitry Andric 
3900b57cec5SDimitry Andric void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
3910b57cec5SDimitry Andric                                            const DemangleCache &DC,
3920b57cec5SDimitry Andric                                            raw_ostream &OS) {
3930b57cec5SDimitry Andric   bool isFirst = true;
3940b57cec5SDimitry Andric   for (StringRef Filename : Files) {
3950b57cec5SDimitry Andric     auto Functions = Coverage.getCoveredFunctions(Filename);
3960b57cec5SDimitry Andric 
3970b57cec5SDimitry Andric     if (isFirst)
3980b57cec5SDimitry Andric       isFirst = false;
3990b57cec5SDimitry Andric     else
4000b57cec5SDimitry Andric       OS << "\n";
4010b57cec5SDimitry Andric 
4020b57cec5SDimitry Andric     std::vector<StringRef> Funcnames;
4030b57cec5SDimitry Andric     for (const auto &F : Functions)
4040b57cec5SDimitry Andric       Funcnames.emplace_back(DC.demangle(F.Name));
4050b57cec5SDimitry Andric     adjustColumnWidths({}, Funcnames);
4060b57cec5SDimitry Andric 
4070b57cec5SDimitry Andric     OS << "File '" << Filename << "':\n";
4080b57cec5SDimitry Andric     OS << column("Name", FunctionReportColumns[0])
4090b57cec5SDimitry Andric        << column("Regions", FunctionReportColumns[1], Column::RightAlignment)
4100b57cec5SDimitry Andric        << column("Miss", FunctionReportColumns[2], Column::RightAlignment)
4110b57cec5SDimitry Andric        << column("Cover", FunctionReportColumns[3], Column::RightAlignment)
4120b57cec5SDimitry Andric        << column("Lines", FunctionReportColumns[4], Column::RightAlignment)
4130b57cec5SDimitry Andric        << column("Miss", FunctionReportColumns[5], Column::RightAlignment)
4140b57cec5SDimitry Andric        << column("Cover", FunctionReportColumns[6], Column::RightAlignment);
415e8d8bef9SDimitry Andric     if (Options.ShowBranchSummary)
416e8d8bef9SDimitry Andric       OS << column("Branches", FunctionReportColumns[7], Column::RightAlignment)
417e8d8bef9SDimitry Andric          << column("Miss", FunctionReportColumns[8], Column::RightAlignment)
418e8d8bef9SDimitry Andric          << column("Cover", FunctionReportColumns[9], Column::RightAlignment);
4195f757f3fSDimitry Andric     if (Options.ShowMCDCSummary)
4205f757f3fSDimitry Andric       OS << column("MC/DC Conditions", FunctionReportColumns[10],
4215f757f3fSDimitry Andric                    Column::RightAlignment)
4225f757f3fSDimitry Andric          << column("Miss", FunctionReportColumns[11], Column::RightAlignment)
4235f757f3fSDimitry Andric          << column("Cover", FunctionReportColumns[12], Column::RightAlignment);
4240b57cec5SDimitry Andric     OS << "\n";
425*0fca6ea1SDimitry Andric     renderDivider(OS, Options, false);
4260b57cec5SDimitry Andric     OS << "\n";
4270b57cec5SDimitry Andric     FunctionCoverageSummary Totals("TOTAL");
4280b57cec5SDimitry Andric     for (const auto &F : Functions) {
4290b57cec5SDimitry Andric       auto Function = FunctionCoverageSummary::get(Coverage, F);
4300b57cec5SDimitry Andric       ++Totals.ExecutionCount;
4310b57cec5SDimitry Andric       Totals.RegionCoverage += Function.RegionCoverage;
4320b57cec5SDimitry Andric       Totals.LineCoverage += Function.LineCoverage;
433e8d8bef9SDimitry Andric       Totals.BranchCoverage += Function.BranchCoverage;
4345f757f3fSDimitry Andric       Totals.MCDCCoverage += Function.MCDCCoverage;
4350b57cec5SDimitry Andric       render(Function, DC, OS);
4360b57cec5SDimitry Andric     }
4370b57cec5SDimitry Andric     if (Totals.ExecutionCount) {
438*0fca6ea1SDimitry Andric       renderDivider(OS, Options, false);
4390b57cec5SDimitry Andric       OS << "\n";
4400b57cec5SDimitry Andric       render(Totals, DC, OS);
4410b57cec5SDimitry Andric     }
4420b57cec5SDimitry Andric   }
4430b57cec5SDimitry Andric }
4440b57cec5SDimitry Andric 
4450b57cec5SDimitry Andric void CoverageReport::prepareSingleFileReport(const StringRef Filename,
4460b57cec5SDimitry Andric     const coverage::CoverageMapping *Coverage,
4470b57cec5SDimitry Andric     const CoverageViewOptions &Options, const unsigned LCP,
4480b57cec5SDimitry Andric     FileCoverageSummary *FileReport, const CoverageFilter *Filters) {
4490b57cec5SDimitry Andric   for (const auto &Group : Coverage->getInstantiationGroups(Filename)) {
4500b57cec5SDimitry Andric     std::vector<FunctionCoverageSummary> InstantiationSummaries;
4510b57cec5SDimitry Andric     for (const coverage::FunctionRecord *F : Group.getInstantiations()) {
4520b57cec5SDimitry Andric       if (!Filters->matches(*Coverage, *F))
4530b57cec5SDimitry Andric         continue;
4540b57cec5SDimitry Andric       auto InstantiationSummary = FunctionCoverageSummary::get(*Coverage, *F);
4550b57cec5SDimitry Andric       FileReport->addInstantiation(InstantiationSummary);
4560b57cec5SDimitry Andric       InstantiationSummaries.push_back(InstantiationSummary);
4570b57cec5SDimitry Andric     }
4580b57cec5SDimitry Andric     if (InstantiationSummaries.empty())
4590b57cec5SDimitry Andric       continue;
4600b57cec5SDimitry Andric 
4610b57cec5SDimitry Andric     auto GroupSummary =
4620b57cec5SDimitry Andric         FunctionCoverageSummary::get(Group, InstantiationSummaries);
4630b57cec5SDimitry Andric 
4640b57cec5SDimitry Andric     if (Options.Debug)
4650b57cec5SDimitry Andric       outs() << "InstantiationGroup: " << GroupSummary.Name << " with "
4660b57cec5SDimitry Andric              << "size = " << Group.size() << "\n";
4670b57cec5SDimitry Andric 
4680b57cec5SDimitry Andric     FileReport->addFunction(GroupSummary);
4690b57cec5SDimitry Andric   }
4700b57cec5SDimitry Andric }
4710b57cec5SDimitry Andric 
4720b57cec5SDimitry Andric std::vector<FileCoverageSummary> CoverageReport::prepareFileReports(
4730b57cec5SDimitry Andric     const coverage::CoverageMapping &Coverage, FileCoverageSummary &Totals,
4740b57cec5SDimitry Andric     ArrayRef<std::string> Files, const CoverageViewOptions &Options,
4750b57cec5SDimitry Andric     const CoverageFilter &Filters) {
4760b57cec5SDimitry Andric   unsigned LCP = getRedundantPrefixLen(Files);
4770b57cec5SDimitry Andric 
4785ffd83dbSDimitry Andric   ThreadPoolStrategy S = hardware_concurrency(Options.NumThreads);
4795ffd83dbSDimitry Andric   if (Options.NumThreads == 0) {
4805ffd83dbSDimitry Andric     // If NumThreads is not specified, create one thread for each input, up to
4815ffd83dbSDimitry Andric     // the number of hardware cores.
4825ffd83dbSDimitry Andric     S = heavyweight_hardware_concurrency(Files.size());
4835ffd83dbSDimitry Andric     S.Limit = true;
4845ffd83dbSDimitry Andric   }
485*0fca6ea1SDimitry Andric   DefaultThreadPool Pool(S);
4860b57cec5SDimitry Andric 
4870b57cec5SDimitry Andric   std::vector<FileCoverageSummary> FileReports;
4880b57cec5SDimitry Andric   FileReports.reserve(Files.size());
4890b57cec5SDimitry Andric 
4900b57cec5SDimitry Andric   for (StringRef Filename : Files) {
4910b57cec5SDimitry Andric     FileReports.emplace_back(Filename.drop_front(LCP));
4920b57cec5SDimitry Andric     Pool.async(&CoverageReport::prepareSingleFileReport, Filename,
4930b57cec5SDimitry Andric                &Coverage, Options, LCP, &FileReports.back(), &Filters);
4940b57cec5SDimitry Andric   }
4950b57cec5SDimitry Andric   Pool.wait();
4960b57cec5SDimitry Andric 
4970b57cec5SDimitry Andric   for (const auto &FileReport : FileReports)
4980b57cec5SDimitry Andric     Totals += FileReport;
4990b57cec5SDimitry Andric 
5000b57cec5SDimitry Andric   return FileReports;
5010b57cec5SDimitry Andric }
5020b57cec5SDimitry Andric 
5030b57cec5SDimitry Andric void CoverageReport::renderFileReports(
5040b57cec5SDimitry Andric     raw_ostream &OS, const CoverageFilters &IgnoreFilenameFilters) const {
5050b57cec5SDimitry Andric   std::vector<std::string> UniqueSourceFiles;
5060b57cec5SDimitry Andric   for (StringRef SF : Coverage.getUniqueSourceFiles()) {
5070b57cec5SDimitry Andric     // Apply ignore source files filters.
5080b57cec5SDimitry Andric     if (!IgnoreFilenameFilters.matchesFilename(SF))
5090b57cec5SDimitry Andric       UniqueSourceFiles.emplace_back(SF.str());
5100b57cec5SDimitry Andric   }
5110b57cec5SDimitry Andric   renderFileReports(OS, UniqueSourceFiles);
5120b57cec5SDimitry Andric }
5130b57cec5SDimitry Andric 
5140b57cec5SDimitry Andric void CoverageReport::renderFileReports(
5150b57cec5SDimitry Andric     raw_ostream &OS, ArrayRef<std::string> Files) const {
5160b57cec5SDimitry Andric   renderFileReports(OS, Files, CoverageFiltersMatchAll());
5170b57cec5SDimitry Andric }
5180b57cec5SDimitry Andric 
5190b57cec5SDimitry Andric void CoverageReport::renderFileReports(
5200b57cec5SDimitry Andric     raw_ostream &OS, ArrayRef<std::string> Files,
5210b57cec5SDimitry Andric     const CoverageFiltersMatchAll &Filters) const {
5220b57cec5SDimitry Andric   FileCoverageSummary Totals("TOTAL");
5230b57cec5SDimitry Andric   auto FileReports =
5240b57cec5SDimitry Andric       prepareFileReports(Coverage, Totals, Files, Options, Filters);
5255f757f3fSDimitry Andric   renderFileReports(OS, FileReports, Totals, Filters.empty());
5265f757f3fSDimitry Andric }
5270b57cec5SDimitry Andric 
5285f757f3fSDimitry Andric void CoverageReport::renderFileReports(
5295f757f3fSDimitry Andric     raw_ostream &OS, const std::vector<FileCoverageSummary> &FileReports,
5305f757f3fSDimitry Andric     const FileCoverageSummary &Totals, bool ShowEmptyFiles) const {
5310b57cec5SDimitry Andric   std::vector<StringRef> Filenames;
532bdd1243dSDimitry Andric   Filenames.reserve(FileReports.size());
5330b57cec5SDimitry Andric   for (const FileCoverageSummary &FCS : FileReports)
5340b57cec5SDimitry Andric     Filenames.emplace_back(FCS.Name);
5350b57cec5SDimitry Andric   adjustColumnWidths(Filenames, {});
5360b57cec5SDimitry Andric 
5370b57cec5SDimitry Andric   OS << column("Filename", FileReportColumns[0]);
5380b57cec5SDimitry Andric   if (Options.ShowRegionSummary)
5390b57cec5SDimitry Andric     OS << column("Regions", FileReportColumns[1], Column::RightAlignment)
5400b57cec5SDimitry Andric        << column("Missed Regions", FileReportColumns[2], Column::RightAlignment)
5410b57cec5SDimitry Andric        << column("Cover", FileReportColumns[3], Column::RightAlignment);
5420b57cec5SDimitry Andric   OS << column("Functions", FileReportColumns[4], Column::RightAlignment)
5430b57cec5SDimitry Andric      << column("Missed Functions", FileReportColumns[5], Column::RightAlignment)
5440b57cec5SDimitry Andric      << column("Executed", FileReportColumns[6], Column::RightAlignment);
5450b57cec5SDimitry Andric   if (Options.ShowInstantiationSummary)
5460b57cec5SDimitry Andric     OS << column("Instantiations", FileReportColumns[7], Column::RightAlignment)
5470b57cec5SDimitry Andric        << column("Missed Insts.", FileReportColumns[8], Column::RightAlignment)
5480b57cec5SDimitry Andric        << column("Executed", FileReportColumns[9], Column::RightAlignment);
5490b57cec5SDimitry Andric   OS << column("Lines", FileReportColumns[10], Column::RightAlignment)
5500b57cec5SDimitry Andric      << column("Missed Lines", FileReportColumns[11], Column::RightAlignment)
551e8d8bef9SDimitry Andric      << column("Cover", FileReportColumns[12], Column::RightAlignment);
552e8d8bef9SDimitry Andric   if (Options.ShowBranchSummary)
553e8d8bef9SDimitry Andric     OS << column("Branches", FileReportColumns[13], Column::RightAlignment)
554e8d8bef9SDimitry Andric        << column("Missed Branches", FileReportColumns[14],
555e8d8bef9SDimitry Andric                  Column::RightAlignment)
556e8d8bef9SDimitry Andric        << column("Cover", FileReportColumns[15], Column::RightAlignment);
5575f757f3fSDimitry Andric   if (Options.ShowMCDCSummary)
5585f757f3fSDimitry Andric     OS << column("MC/DC Conditions", FileReportColumns[16],
5595f757f3fSDimitry Andric                  Column::RightAlignment)
5605f757f3fSDimitry Andric        << column("Missed Conditions", FileReportColumns[17],
5615f757f3fSDimitry Andric                  Column::RightAlignment)
5625f757f3fSDimitry Andric        << column("Cover", FileReportColumns[18], Column::RightAlignment);
563e8d8bef9SDimitry Andric   OS << "\n";
564*0fca6ea1SDimitry Andric   renderDivider(OS, Options, true);
5650b57cec5SDimitry Andric   OS << "\n";
5660b57cec5SDimitry Andric 
5675f757f3fSDimitry Andric   std::vector<const FileCoverageSummary *> EmptyFiles;
5680b57cec5SDimitry Andric   for (const FileCoverageSummary &FCS : FileReports) {
5690b57cec5SDimitry Andric     if (FCS.FunctionCoverage.getNumFunctions())
5700b57cec5SDimitry Andric       render(FCS, OS);
5710b57cec5SDimitry Andric     else
5725f757f3fSDimitry Andric       EmptyFiles.push_back(&FCS);
5730b57cec5SDimitry Andric   }
5740b57cec5SDimitry Andric 
5755f757f3fSDimitry Andric   if (!EmptyFiles.empty() && ShowEmptyFiles) {
5760b57cec5SDimitry Andric     OS << "\n"
5770b57cec5SDimitry Andric        << "Files which contain no functions:\n";
5780b57cec5SDimitry Andric 
5795f757f3fSDimitry Andric     for (auto FCS : EmptyFiles)
5805f757f3fSDimitry Andric       render(*FCS, OS);
5810b57cec5SDimitry Andric   }
5820b57cec5SDimitry Andric 
583*0fca6ea1SDimitry Andric   renderDivider(OS, Options, true);
5840b57cec5SDimitry Andric   OS << "\n";
5850b57cec5SDimitry Andric   render(Totals, OS);
5860b57cec5SDimitry Andric }
5870b57cec5SDimitry Andric 
5885f757f3fSDimitry Andric Expected<FileCoverageSummary> DirectoryCoverageReport::prepareDirectoryReports(
5895f757f3fSDimitry Andric     ArrayRef<std::string> SourceFiles) {
5905f757f3fSDimitry Andric   std::vector<StringRef> Files(SourceFiles.begin(), SourceFiles.end());
5915f757f3fSDimitry Andric 
5925f757f3fSDimitry Andric   unsigned RootLCP = getRedundantPrefixLen(Files, 0);
5935f757f3fSDimitry Andric   auto LCPath = Files.front().substr(0, RootLCP);
5945f757f3fSDimitry Andric 
5955f757f3fSDimitry Andric   ThreadPoolStrategy PoolS = hardware_concurrency(Options.NumThreads);
5965f757f3fSDimitry Andric   if (Options.NumThreads == 0) {
5975f757f3fSDimitry Andric     PoolS = heavyweight_hardware_concurrency(Files.size());
5985f757f3fSDimitry Andric     PoolS.Limit = true;
5995f757f3fSDimitry Andric   }
600*0fca6ea1SDimitry Andric   DefaultThreadPool Pool(PoolS);
6015f757f3fSDimitry Andric 
6025f757f3fSDimitry Andric   TPool = &Pool;
6035f757f3fSDimitry Andric   LCPStack = {RootLCP};
6045f757f3fSDimitry Andric   FileCoverageSummary RootTotals(LCPath);
6055f757f3fSDimitry Andric   if (auto E = prepareSubDirectoryReports(Files, &RootTotals))
6065f757f3fSDimitry Andric     return {std::move(E)};
6075f757f3fSDimitry Andric   return {std::move(RootTotals)};
6085f757f3fSDimitry Andric }
6095f757f3fSDimitry Andric 
6105f757f3fSDimitry Andric /// Filter out files in LCPStack.back(), group others by subdirectory name
6115f757f3fSDimitry Andric /// and recurse on them. After returning from all subdirectories, call
6125f757f3fSDimitry Andric /// generateSubDirectoryReport(). \p Files must be non-empty. The
6135f757f3fSDimitry Andric /// FileCoverageSummary of this directory will be added to \p Totals.
6145f757f3fSDimitry Andric Error DirectoryCoverageReport::prepareSubDirectoryReports(
6155f757f3fSDimitry Andric     const ArrayRef<StringRef> &Files, FileCoverageSummary *Totals) {
6165f757f3fSDimitry Andric   assert(!Files.empty() && "Files must have at least one element");
6175f757f3fSDimitry Andric 
6185f757f3fSDimitry Andric   auto LCP = LCPStack.back();
6195f757f3fSDimitry Andric   auto LCPath = Files.front().substr(0, LCP).str();
6205f757f3fSDimitry Andric 
6215f757f3fSDimitry Andric   // Use ordered map to keep entries in order.
6225f757f3fSDimitry Andric   SubFileReports SubFiles;
6235f757f3fSDimitry Andric   SubDirReports SubDirs;
6245f757f3fSDimitry Andric   for (auto &&File : Files) {
6255f757f3fSDimitry Andric     auto SubPath = File.substr(LCPath.size());
6265f757f3fSDimitry Andric     SmallVector<char, 128> NativeSubPath;
6275f757f3fSDimitry Andric     sys::path::native(SubPath, NativeSubPath);
6285f757f3fSDimitry Andric     StringRef NativeSubPathRef(NativeSubPath.data(), NativeSubPath.size());
6295f757f3fSDimitry Andric 
6305f757f3fSDimitry Andric     auto I = sys::path::begin(NativeSubPathRef);
6315f757f3fSDimitry Andric     auto E = sys::path::end(NativeSubPathRef);
6325f757f3fSDimitry Andric     assert(I != E && "Such case should have been filtered out in the caller");
6335f757f3fSDimitry Andric 
6345f757f3fSDimitry Andric     auto Name = SubPath.substr(0, I->size());
6355f757f3fSDimitry Andric     if (++I == E) {
6365f757f3fSDimitry Andric       auto Iter = SubFiles.insert_or_assign(Name, SubPath).first;
6375f757f3fSDimitry Andric       // Makes files reporting overlap with subdir reporting.
6385f757f3fSDimitry Andric       TPool->async(&CoverageReport::prepareSingleFileReport, File, &Coverage,
6395f757f3fSDimitry Andric                    Options, LCP, &Iter->second, &Filters);
6405f757f3fSDimitry Andric     } else {
6415f757f3fSDimitry Andric       SubDirs[Name].second.push_back(File);
6425f757f3fSDimitry Andric     }
6435f757f3fSDimitry Andric   }
6445f757f3fSDimitry Andric 
6455f757f3fSDimitry Andric   // Call recursively on subdirectories.
6465f757f3fSDimitry Andric   for (auto &&KV : SubDirs) {
6475f757f3fSDimitry Andric     auto &V = KV.second;
6485f757f3fSDimitry Andric     if (V.second.size() == 1) {
6495f757f3fSDimitry Andric       // If there's only one file in that subdirectory, we don't bother to
6505f757f3fSDimitry Andric       // recurse on it further.
6515f757f3fSDimitry Andric       V.first.Name = V.second.front().substr(LCP);
6525f757f3fSDimitry Andric       TPool->async(&CoverageReport::prepareSingleFileReport, V.second.front(),
6535f757f3fSDimitry Andric                    &Coverage, Options, LCP, &V.first, &Filters);
6545f757f3fSDimitry Andric     } else {
6555f757f3fSDimitry Andric       auto SubDirLCP = getRedundantPrefixLen(V.second, LCP);
6565f757f3fSDimitry Andric       V.first.Name = V.second.front().substr(LCP, SubDirLCP);
6575f757f3fSDimitry Andric       LCPStack.push_back(LCP + SubDirLCP);
6585f757f3fSDimitry Andric       if (auto E = prepareSubDirectoryReports(V.second, &V.first))
6595f757f3fSDimitry Andric         return E;
6605f757f3fSDimitry Andric     }
6615f757f3fSDimitry Andric   }
6625f757f3fSDimitry Andric 
6635f757f3fSDimitry Andric   TPool->wait();
6645f757f3fSDimitry Andric 
6655f757f3fSDimitry Andric   FileCoverageSummary CurrentTotals(LCPath);
6665f757f3fSDimitry Andric   for (auto &&KV : SubFiles)
6675f757f3fSDimitry Andric     CurrentTotals += KV.second;
6685f757f3fSDimitry Andric   for (auto &&KV : SubDirs)
6695f757f3fSDimitry Andric     CurrentTotals += KV.second.first;
6705f757f3fSDimitry Andric   *Totals += CurrentTotals;
6715f757f3fSDimitry Andric 
6725f757f3fSDimitry Andric   if (auto E = generateSubDirectoryReport(
6735f757f3fSDimitry Andric           std::move(SubFiles), std::move(SubDirs), std::move(CurrentTotals)))
6745f757f3fSDimitry Andric     return E;
6755f757f3fSDimitry Andric 
6765f757f3fSDimitry Andric   LCPStack.pop_back();
6775f757f3fSDimitry Andric   return Error::success();
6785f757f3fSDimitry Andric }
6795f757f3fSDimitry Andric 
6800b57cec5SDimitry Andric } // end namespace llvm
681