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