10b57cec5SDimitry Andric //===- SourceCoverageView.cpp - Code coverage view for source code --------===// 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 /// \file This class implements rendering for code coverage of source code. 100b57cec5SDimitry Andric /// 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric 130b57cec5SDimitry Andric #include "SourceCoverageView.h" 140b57cec5SDimitry Andric #include "SourceCoverageViewHTML.h" 150b57cec5SDimitry Andric #include "SourceCoverageViewText.h" 160b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 170b57cec5SDimitry Andric #include "llvm/ADT/StringExtras.h" 180b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 190b57cec5SDimitry Andric #include "llvm/Support/LineIterator.h" 200b57cec5SDimitry Andric #include "llvm/Support/Path.h" 210b57cec5SDimitry Andric 220b57cec5SDimitry Andric using namespace llvm; 230b57cec5SDimitry Andric 240b57cec5SDimitry Andric void CoveragePrinter::StreamDestructor::operator()(raw_ostream *OS) const { 250b57cec5SDimitry Andric if (OS == &outs()) 260b57cec5SDimitry Andric return; 270b57cec5SDimitry Andric delete OS; 280b57cec5SDimitry Andric } 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric std::string CoveragePrinter::getOutputPath(StringRef Path, StringRef Extension, 310b57cec5SDimitry Andric bool InToplevel, 320b57cec5SDimitry Andric bool Relative) const { 330b57cec5SDimitry Andric assert(!Extension.empty() && "The file extension may not be empty"); 340b57cec5SDimitry Andric 350b57cec5SDimitry Andric SmallString<256> FullPath; 360b57cec5SDimitry Andric 370b57cec5SDimitry Andric if (!Relative) 380b57cec5SDimitry Andric FullPath.append(Opts.ShowOutputDirectory); 390b57cec5SDimitry Andric 400b57cec5SDimitry Andric if (!InToplevel) 410b57cec5SDimitry Andric sys::path::append(FullPath, getCoverageDir()); 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric SmallString<256> ParentPath = sys::path::parent_path(Path); 440b57cec5SDimitry Andric sys::path::remove_dots(ParentPath, /*remove_dot_dots=*/true); 450b57cec5SDimitry Andric sys::path::append(FullPath, sys::path::relative_path(ParentPath)); 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric auto PathFilename = (sys::path::filename(Path) + "." + Extension).str(); 480b57cec5SDimitry Andric sys::path::append(FullPath, PathFilename); 490b57cec5SDimitry Andric sys::path::native(FullPath); 500b57cec5SDimitry Andric 515ffd83dbSDimitry Andric return std::string(FullPath.str()); 520b57cec5SDimitry Andric } 530b57cec5SDimitry Andric 540b57cec5SDimitry Andric Expected<CoveragePrinter::OwnedStream> 550b57cec5SDimitry Andric CoveragePrinter::createOutputStream(StringRef Path, StringRef Extension, 560b57cec5SDimitry Andric bool InToplevel) const { 570b57cec5SDimitry Andric if (!Opts.hasOutputDirectory()) 580b57cec5SDimitry Andric return OwnedStream(&outs()); 590b57cec5SDimitry Andric 600b57cec5SDimitry Andric std::string FullPath = getOutputPath(Path, Extension, InToplevel, false); 610b57cec5SDimitry Andric 620b57cec5SDimitry Andric auto ParentDir = sys::path::parent_path(FullPath); 630b57cec5SDimitry Andric if (auto E = sys::fs::create_directories(ParentDir)) 640b57cec5SDimitry Andric return errorCodeToError(E); 650b57cec5SDimitry Andric 660b57cec5SDimitry Andric std::error_code E; 670b57cec5SDimitry Andric raw_ostream *RawStream = 680b57cec5SDimitry Andric new raw_fd_ostream(FullPath, E, sys::fs::FA_Read | sys::fs::FA_Write); 690b57cec5SDimitry Andric auto OS = CoveragePrinter::OwnedStream(RawStream); 700b57cec5SDimitry Andric if (E) 710b57cec5SDimitry Andric return errorCodeToError(E); 720b57cec5SDimitry Andric return std::move(OS); 730b57cec5SDimitry Andric } 740b57cec5SDimitry Andric 750b57cec5SDimitry Andric std::unique_ptr<CoveragePrinter> 760b57cec5SDimitry Andric CoveragePrinter::create(const CoverageViewOptions &Opts) { 770b57cec5SDimitry Andric switch (Opts.Format) { 780b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Text: 798bcb0991SDimitry Andric return std::make_unique<CoveragePrinterText>(Opts); 800b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::HTML: 818bcb0991SDimitry Andric return std::make_unique<CoveragePrinterHTML>(Opts); 820b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Lcov: 830b57cec5SDimitry Andric // Unreachable because CodeCoverage.cpp should terminate with an error 840b57cec5SDimitry Andric // before we get here. 850b57cec5SDimitry Andric llvm_unreachable("Lcov format is not supported!"); 860b57cec5SDimitry Andric } 870b57cec5SDimitry Andric llvm_unreachable("Unknown coverage output format!"); 880b57cec5SDimitry Andric } 890b57cec5SDimitry Andric 900b57cec5SDimitry Andric unsigned SourceCoverageView::getFirstUncoveredLineNo() { 910b57cec5SDimitry Andric const auto MinSegIt = find_if(CoverageInfo, [](const CoverageSegment &S) { 920b57cec5SDimitry Andric return S.HasCount && S.Count == 0; 930b57cec5SDimitry Andric }); 940b57cec5SDimitry Andric 950b57cec5SDimitry Andric // There is no uncovered line, return zero. 960b57cec5SDimitry Andric if (MinSegIt == CoverageInfo.end()) 970b57cec5SDimitry Andric return 0; 980b57cec5SDimitry Andric 990b57cec5SDimitry Andric return (*MinSegIt).Line; 1000b57cec5SDimitry Andric } 1010b57cec5SDimitry Andric 1020b57cec5SDimitry Andric std::string SourceCoverageView::formatCount(uint64_t N) { 1030b57cec5SDimitry Andric std::string Number = utostr(N); 1040b57cec5SDimitry Andric int Len = Number.size(); 1050b57cec5SDimitry Andric if (Len <= 3) 1060b57cec5SDimitry Andric return Number; 1070b57cec5SDimitry Andric int IntLen = Len % 3 == 0 ? 3 : Len % 3; 1080b57cec5SDimitry Andric std::string Result(Number.data(), IntLen); 1090b57cec5SDimitry Andric if (IntLen != 3) { 1100b57cec5SDimitry Andric Result.push_back('.'); 1110b57cec5SDimitry Andric Result += Number.substr(IntLen, 3 - IntLen); 1120b57cec5SDimitry Andric } 1130b57cec5SDimitry Andric Result.push_back(" kMGTPEZY"[(Len - 1) / 3]); 1140b57cec5SDimitry Andric return Result; 1150b57cec5SDimitry Andric } 1160b57cec5SDimitry Andric 1170b57cec5SDimitry Andric bool SourceCoverageView::shouldRenderRegionMarkers( 1180b57cec5SDimitry Andric const LineCoverageStats &LCS) const { 1190b57cec5SDimitry Andric if (!getOptions().ShowRegionMarkers) 1200b57cec5SDimitry Andric return false; 1210b57cec5SDimitry Andric 1220b57cec5SDimitry Andric CoverageSegmentArray Segments = LCS.getLineSegments(); 1230b57cec5SDimitry Andric if (Segments.empty()) 1240b57cec5SDimitry Andric return false; 1250b57cec5SDimitry Andric for (unsigned I = 0, E = Segments.size() - 1; I < E; ++I) { 1260b57cec5SDimitry Andric const auto *CurSeg = Segments[I]; 1270b57cec5SDimitry Andric if (!CurSeg->IsRegionEntry || CurSeg->Count == LCS.getExecutionCount()) 1280b57cec5SDimitry Andric continue; 1290b57cec5SDimitry Andric return true; 1300b57cec5SDimitry Andric } 1310b57cec5SDimitry Andric return false; 1320b57cec5SDimitry Andric } 1330b57cec5SDimitry Andric 1340b57cec5SDimitry Andric bool SourceCoverageView::hasSubViews() const { 135*e8d8bef9SDimitry Andric return !ExpansionSubViews.empty() || !InstantiationSubViews.empty() || 136*e8d8bef9SDimitry Andric !BranchSubViews.empty(); 1370b57cec5SDimitry Andric } 1380b57cec5SDimitry Andric 1390b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 1400b57cec5SDimitry Andric SourceCoverageView::create(StringRef SourceName, const MemoryBuffer &File, 1410b57cec5SDimitry Andric const CoverageViewOptions &Options, 1420b57cec5SDimitry Andric CoverageData &&CoverageInfo) { 1430b57cec5SDimitry Andric switch (Options.Format) { 1440b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Text: 1458bcb0991SDimitry Andric return std::make_unique<SourceCoverageViewText>( 1460b57cec5SDimitry Andric SourceName, File, Options, std::move(CoverageInfo)); 1470b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::HTML: 1488bcb0991SDimitry Andric return std::make_unique<SourceCoverageViewHTML>( 1490b57cec5SDimitry Andric SourceName, File, Options, std::move(CoverageInfo)); 1500b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Lcov: 1510b57cec5SDimitry Andric // Unreachable because CodeCoverage.cpp should terminate with an error 1520b57cec5SDimitry Andric // before we get here. 1530b57cec5SDimitry Andric llvm_unreachable("Lcov format is not supported!"); 1540b57cec5SDimitry Andric } 1550b57cec5SDimitry Andric llvm_unreachable("Unknown coverage output format!"); 1560b57cec5SDimitry Andric } 1570b57cec5SDimitry Andric 1580b57cec5SDimitry Andric std::string SourceCoverageView::getSourceName() const { 1590b57cec5SDimitry Andric SmallString<128> SourceText(SourceName); 1600b57cec5SDimitry Andric sys::path::remove_dots(SourceText, /*remove_dot_dots=*/true); 1610b57cec5SDimitry Andric sys::path::native(SourceText); 1625ffd83dbSDimitry Andric return std::string(SourceText.str()); 1630b57cec5SDimitry Andric } 1640b57cec5SDimitry Andric 1650b57cec5SDimitry Andric void SourceCoverageView::addExpansion( 1660b57cec5SDimitry Andric const CounterMappingRegion &Region, 1670b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> View) { 1680b57cec5SDimitry Andric ExpansionSubViews.emplace_back(Region, std::move(View)); 1690b57cec5SDimitry Andric } 1700b57cec5SDimitry Andric 171*e8d8bef9SDimitry Andric void SourceCoverageView::addBranch(unsigned Line, 172*e8d8bef9SDimitry Andric ArrayRef<CountedRegion> Regions, 173*e8d8bef9SDimitry Andric std::unique_ptr<SourceCoverageView> View) { 174*e8d8bef9SDimitry Andric BranchSubViews.emplace_back(Line, Regions, std::move(View)); 175*e8d8bef9SDimitry Andric } 176*e8d8bef9SDimitry Andric 1770b57cec5SDimitry Andric void SourceCoverageView::addInstantiation( 1780b57cec5SDimitry Andric StringRef FunctionName, unsigned Line, 1790b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> View) { 1800b57cec5SDimitry Andric InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View)); 1810b57cec5SDimitry Andric } 1820b57cec5SDimitry Andric 1830b57cec5SDimitry Andric void SourceCoverageView::print(raw_ostream &OS, bool WholeFile, 1840b57cec5SDimitry Andric bool ShowSourceName, bool ShowTitle, 1850b57cec5SDimitry Andric unsigned ViewDepth) { 1860b57cec5SDimitry Andric if (ShowTitle) 1870b57cec5SDimitry Andric renderTitle(OS, "Coverage Report"); 1880b57cec5SDimitry Andric 1890b57cec5SDimitry Andric renderViewHeader(OS); 1900b57cec5SDimitry Andric 1910b57cec5SDimitry Andric if (ShowSourceName) 1920b57cec5SDimitry Andric renderSourceName(OS, WholeFile); 1930b57cec5SDimitry Andric 1940b57cec5SDimitry Andric renderTableHeader(OS, (ViewDepth > 0) ? 0 : getFirstUncoveredLineNo(), 1950b57cec5SDimitry Andric ViewDepth); 1960b57cec5SDimitry Andric 197*e8d8bef9SDimitry Andric // We need the expansions, instantiations, and branches sorted so we can go 198*e8d8bef9SDimitry Andric // through them while we iterate lines. 1990b57cec5SDimitry Andric llvm::stable_sort(ExpansionSubViews); 2000b57cec5SDimitry Andric llvm::stable_sort(InstantiationSubViews); 201*e8d8bef9SDimitry Andric llvm::stable_sort(BranchSubViews); 2020b57cec5SDimitry Andric auto NextESV = ExpansionSubViews.begin(); 2030b57cec5SDimitry Andric auto EndESV = ExpansionSubViews.end(); 2040b57cec5SDimitry Andric auto NextISV = InstantiationSubViews.begin(); 2050b57cec5SDimitry Andric auto EndISV = InstantiationSubViews.end(); 206*e8d8bef9SDimitry Andric auto NextBRV = BranchSubViews.begin(); 207*e8d8bef9SDimitry Andric auto EndBRV = BranchSubViews.end(); 2080b57cec5SDimitry Andric 2090b57cec5SDimitry Andric // Get the coverage information for the file. 2100b57cec5SDimitry Andric auto StartSegment = CoverageInfo.begin(); 2110b57cec5SDimitry Andric auto EndSegment = CoverageInfo.end(); 2120b57cec5SDimitry Andric LineCoverageIterator LCI{CoverageInfo, 1}; 2130b57cec5SDimitry Andric LineCoverageIterator LCIEnd = LCI.getEnd(); 2140b57cec5SDimitry Andric 2150b57cec5SDimitry Andric unsigned FirstLine = StartSegment != EndSegment ? StartSegment->Line : 0; 2160b57cec5SDimitry Andric for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof(); 2170b57cec5SDimitry Andric ++LI, ++LCI) { 2180b57cec5SDimitry Andric // If we aren't rendering the whole file, we need to filter out the prologue 2190b57cec5SDimitry Andric // and epilogue. 2200b57cec5SDimitry Andric if (!WholeFile) { 2210b57cec5SDimitry Andric if (LCI == LCIEnd) 2220b57cec5SDimitry Andric break; 2230b57cec5SDimitry Andric else if (LI.line_number() < FirstLine) 2240b57cec5SDimitry Andric continue; 2250b57cec5SDimitry Andric } 2260b57cec5SDimitry Andric 2270b57cec5SDimitry Andric renderLinePrefix(OS, ViewDepth); 2280b57cec5SDimitry Andric if (getOptions().ShowLineNumbers) 2290b57cec5SDimitry Andric renderLineNumberColumn(OS, LI.line_number()); 2300b57cec5SDimitry Andric 2310b57cec5SDimitry Andric if (getOptions().ShowLineStats) 2320b57cec5SDimitry Andric renderLineCoverageColumn(OS, *LCI); 2330b57cec5SDimitry Andric 2340b57cec5SDimitry Andric // If there are expansion subviews, we want to highlight the first one. 2350b57cec5SDimitry Andric unsigned ExpansionColumn = 0; 2360b57cec5SDimitry Andric if (NextESV != EndESV && NextESV->getLine() == LI.line_number() && 2370b57cec5SDimitry Andric getOptions().Colors) 2380b57cec5SDimitry Andric ExpansionColumn = NextESV->getStartCol(); 2390b57cec5SDimitry Andric 2400b57cec5SDimitry Andric // Display the source code for the current line. 2410b57cec5SDimitry Andric renderLine(OS, {*LI, LI.line_number()}, *LCI, ExpansionColumn, ViewDepth); 2420b57cec5SDimitry Andric 2430b57cec5SDimitry Andric // Show the region markers. 2440b57cec5SDimitry Andric if (shouldRenderRegionMarkers(*LCI)) 2450b57cec5SDimitry Andric renderRegionMarkers(OS, *LCI, ViewDepth); 2460b57cec5SDimitry Andric 247*e8d8bef9SDimitry Andric // Show the expansions, instantiations, and branches for this line. 2480b57cec5SDimitry Andric bool RenderedSubView = false; 2490b57cec5SDimitry Andric for (; NextESV != EndESV && NextESV->getLine() == LI.line_number(); 2500b57cec5SDimitry Andric ++NextESV) { 2510b57cec5SDimitry Andric renderViewDivider(OS, ViewDepth + 1); 2520b57cec5SDimitry Andric 2530b57cec5SDimitry Andric // Re-render the current line and highlight the expansion range for 2540b57cec5SDimitry Andric // this subview. 2550b57cec5SDimitry Andric if (RenderedSubView) { 2560b57cec5SDimitry Andric ExpansionColumn = NextESV->getStartCol(); 2570b57cec5SDimitry Andric renderExpansionSite(OS, {*LI, LI.line_number()}, *LCI, ExpansionColumn, 2580b57cec5SDimitry Andric ViewDepth); 2590b57cec5SDimitry Andric renderViewDivider(OS, ViewDepth + 1); 2600b57cec5SDimitry Andric } 2610b57cec5SDimitry Andric 2620b57cec5SDimitry Andric renderExpansionView(OS, *NextESV, ViewDepth + 1); 2630b57cec5SDimitry Andric RenderedSubView = true; 2640b57cec5SDimitry Andric } 2650b57cec5SDimitry Andric for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) { 2660b57cec5SDimitry Andric renderViewDivider(OS, ViewDepth + 1); 2670b57cec5SDimitry Andric renderInstantiationView(OS, *NextISV, ViewDepth + 1); 2680b57cec5SDimitry Andric RenderedSubView = true; 2690b57cec5SDimitry Andric } 270*e8d8bef9SDimitry Andric for (; NextBRV != EndBRV && NextBRV->Line == LI.line_number(); ++NextBRV) { 271*e8d8bef9SDimitry Andric renderViewDivider(OS, ViewDepth + 1); 272*e8d8bef9SDimitry Andric renderBranchView(OS, *NextBRV, ViewDepth + 1); 273*e8d8bef9SDimitry Andric RenderedSubView = true; 274*e8d8bef9SDimitry Andric } 2750b57cec5SDimitry Andric if (RenderedSubView) 2760b57cec5SDimitry Andric renderViewDivider(OS, ViewDepth + 1); 2770b57cec5SDimitry Andric renderLineSuffix(OS, ViewDepth); 2780b57cec5SDimitry Andric } 2790b57cec5SDimitry Andric 2800b57cec5SDimitry Andric renderViewFooter(OS); 2810b57cec5SDimitry Andric } 282