1*0b57cec5SDimitry Andric //===- SourceCoverageView.cpp - Code coverage view for source code --------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric /// 9*0b57cec5SDimitry Andric /// \file This class implements rendering for code coverage of source code. 10*0b57cec5SDimitry Andric /// 11*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 12*0b57cec5SDimitry Andric 13*0b57cec5SDimitry Andric #include "SourceCoverageView.h" 14*0b57cec5SDimitry Andric #include "SourceCoverageViewHTML.h" 15*0b57cec5SDimitry Andric #include "SourceCoverageViewText.h" 16*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 17*0b57cec5SDimitry Andric #include "llvm/ADT/StringExtras.h" 18*0b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 19*0b57cec5SDimitry Andric #include "llvm/Support/LineIterator.h" 20*0b57cec5SDimitry Andric #include "llvm/Support/Path.h" 21*0b57cec5SDimitry Andric 22*0b57cec5SDimitry Andric using namespace llvm; 23*0b57cec5SDimitry Andric 24*0b57cec5SDimitry Andric void CoveragePrinter::StreamDestructor::operator()(raw_ostream *OS) const { 25*0b57cec5SDimitry Andric if (OS == &outs()) 26*0b57cec5SDimitry Andric return; 27*0b57cec5SDimitry Andric delete OS; 28*0b57cec5SDimitry Andric } 29*0b57cec5SDimitry Andric 30*0b57cec5SDimitry Andric std::string CoveragePrinter::getOutputPath(StringRef Path, StringRef Extension, 31*0b57cec5SDimitry Andric bool InToplevel, 32*0b57cec5SDimitry Andric bool Relative) const { 33*0b57cec5SDimitry Andric assert(!Extension.empty() && "The file extension may not be empty"); 34*0b57cec5SDimitry Andric 35*0b57cec5SDimitry Andric SmallString<256> FullPath; 36*0b57cec5SDimitry Andric 37*0b57cec5SDimitry Andric if (!Relative) 38*0b57cec5SDimitry Andric FullPath.append(Opts.ShowOutputDirectory); 39*0b57cec5SDimitry Andric 40*0b57cec5SDimitry Andric if (!InToplevel) 41*0b57cec5SDimitry Andric sys::path::append(FullPath, getCoverageDir()); 42*0b57cec5SDimitry Andric 43*0b57cec5SDimitry Andric SmallString<256> ParentPath = sys::path::parent_path(Path); 44*0b57cec5SDimitry Andric sys::path::remove_dots(ParentPath, /*remove_dot_dots=*/true); 45*0b57cec5SDimitry Andric sys::path::append(FullPath, sys::path::relative_path(ParentPath)); 46*0b57cec5SDimitry Andric 47*0b57cec5SDimitry Andric auto PathFilename = (sys::path::filename(Path) + "." + Extension).str(); 48*0b57cec5SDimitry Andric sys::path::append(FullPath, PathFilename); 49*0b57cec5SDimitry Andric sys::path::native(FullPath); 50*0b57cec5SDimitry Andric 51*0b57cec5SDimitry Andric return FullPath.str(); 52*0b57cec5SDimitry Andric } 53*0b57cec5SDimitry Andric 54*0b57cec5SDimitry Andric Expected<CoveragePrinter::OwnedStream> 55*0b57cec5SDimitry Andric CoveragePrinter::createOutputStream(StringRef Path, StringRef Extension, 56*0b57cec5SDimitry Andric bool InToplevel) const { 57*0b57cec5SDimitry Andric if (!Opts.hasOutputDirectory()) 58*0b57cec5SDimitry Andric return OwnedStream(&outs()); 59*0b57cec5SDimitry Andric 60*0b57cec5SDimitry Andric std::string FullPath = getOutputPath(Path, Extension, InToplevel, false); 61*0b57cec5SDimitry Andric 62*0b57cec5SDimitry Andric auto ParentDir = sys::path::parent_path(FullPath); 63*0b57cec5SDimitry Andric if (auto E = sys::fs::create_directories(ParentDir)) 64*0b57cec5SDimitry Andric return errorCodeToError(E); 65*0b57cec5SDimitry Andric 66*0b57cec5SDimitry Andric std::error_code E; 67*0b57cec5SDimitry Andric raw_ostream *RawStream = 68*0b57cec5SDimitry Andric new raw_fd_ostream(FullPath, E, sys::fs::FA_Read | sys::fs::FA_Write); 69*0b57cec5SDimitry Andric auto OS = CoveragePrinter::OwnedStream(RawStream); 70*0b57cec5SDimitry Andric if (E) 71*0b57cec5SDimitry Andric return errorCodeToError(E); 72*0b57cec5SDimitry Andric return std::move(OS); 73*0b57cec5SDimitry Andric } 74*0b57cec5SDimitry Andric 75*0b57cec5SDimitry Andric std::unique_ptr<CoveragePrinter> 76*0b57cec5SDimitry Andric CoveragePrinter::create(const CoverageViewOptions &Opts) { 77*0b57cec5SDimitry Andric switch (Opts.Format) { 78*0b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Text: 79*0b57cec5SDimitry Andric return llvm::make_unique<CoveragePrinterText>(Opts); 80*0b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::HTML: 81*0b57cec5SDimitry Andric return llvm::make_unique<CoveragePrinterHTML>(Opts); 82*0b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Lcov: 83*0b57cec5SDimitry Andric // Unreachable because CodeCoverage.cpp should terminate with an error 84*0b57cec5SDimitry Andric // before we get here. 85*0b57cec5SDimitry Andric llvm_unreachable("Lcov format is not supported!"); 86*0b57cec5SDimitry Andric } 87*0b57cec5SDimitry Andric llvm_unreachable("Unknown coverage output format!"); 88*0b57cec5SDimitry Andric } 89*0b57cec5SDimitry Andric 90*0b57cec5SDimitry Andric unsigned SourceCoverageView::getFirstUncoveredLineNo() { 91*0b57cec5SDimitry Andric const auto MinSegIt = find_if(CoverageInfo, [](const CoverageSegment &S) { 92*0b57cec5SDimitry Andric return S.HasCount && S.Count == 0; 93*0b57cec5SDimitry Andric }); 94*0b57cec5SDimitry Andric 95*0b57cec5SDimitry Andric // There is no uncovered line, return zero. 96*0b57cec5SDimitry Andric if (MinSegIt == CoverageInfo.end()) 97*0b57cec5SDimitry Andric return 0; 98*0b57cec5SDimitry Andric 99*0b57cec5SDimitry Andric return (*MinSegIt).Line; 100*0b57cec5SDimitry Andric } 101*0b57cec5SDimitry Andric 102*0b57cec5SDimitry Andric std::string SourceCoverageView::formatCount(uint64_t N) { 103*0b57cec5SDimitry Andric std::string Number = utostr(N); 104*0b57cec5SDimitry Andric int Len = Number.size(); 105*0b57cec5SDimitry Andric if (Len <= 3) 106*0b57cec5SDimitry Andric return Number; 107*0b57cec5SDimitry Andric int IntLen = Len % 3 == 0 ? 3 : Len % 3; 108*0b57cec5SDimitry Andric std::string Result(Number.data(), IntLen); 109*0b57cec5SDimitry Andric if (IntLen != 3) { 110*0b57cec5SDimitry Andric Result.push_back('.'); 111*0b57cec5SDimitry Andric Result += Number.substr(IntLen, 3 - IntLen); 112*0b57cec5SDimitry Andric } 113*0b57cec5SDimitry Andric Result.push_back(" kMGTPEZY"[(Len - 1) / 3]); 114*0b57cec5SDimitry Andric return Result; 115*0b57cec5SDimitry Andric } 116*0b57cec5SDimitry Andric 117*0b57cec5SDimitry Andric bool SourceCoverageView::shouldRenderRegionMarkers( 118*0b57cec5SDimitry Andric const LineCoverageStats &LCS) const { 119*0b57cec5SDimitry Andric if (!getOptions().ShowRegionMarkers) 120*0b57cec5SDimitry Andric return false; 121*0b57cec5SDimitry Andric 122*0b57cec5SDimitry Andric CoverageSegmentArray Segments = LCS.getLineSegments(); 123*0b57cec5SDimitry Andric if (Segments.empty()) 124*0b57cec5SDimitry Andric return false; 125*0b57cec5SDimitry Andric for (unsigned I = 0, E = Segments.size() - 1; I < E; ++I) { 126*0b57cec5SDimitry Andric const auto *CurSeg = Segments[I]; 127*0b57cec5SDimitry Andric if (!CurSeg->IsRegionEntry || CurSeg->Count == LCS.getExecutionCount()) 128*0b57cec5SDimitry Andric continue; 129*0b57cec5SDimitry Andric return true; 130*0b57cec5SDimitry Andric } 131*0b57cec5SDimitry Andric return false; 132*0b57cec5SDimitry Andric } 133*0b57cec5SDimitry Andric 134*0b57cec5SDimitry Andric bool SourceCoverageView::hasSubViews() const { 135*0b57cec5SDimitry Andric return !ExpansionSubViews.empty() || !InstantiationSubViews.empty(); 136*0b57cec5SDimitry Andric } 137*0b57cec5SDimitry Andric 138*0b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 139*0b57cec5SDimitry Andric SourceCoverageView::create(StringRef SourceName, const MemoryBuffer &File, 140*0b57cec5SDimitry Andric const CoverageViewOptions &Options, 141*0b57cec5SDimitry Andric CoverageData &&CoverageInfo) { 142*0b57cec5SDimitry Andric switch (Options.Format) { 143*0b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Text: 144*0b57cec5SDimitry Andric return llvm::make_unique<SourceCoverageViewText>( 145*0b57cec5SDimitry Andric SourceName, File, Options, std::move(CoverageInfo)); 146*0b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::HTML: 147*0b57cec5SDimitry Andric return llvm::make_unique<SourceCoverageViewHTML>( 148*0b57cec5SDimitry Andric SourceName, File, Options, std::move(CoverageInfo)); 149*0b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Lcov: 150*0b57cec5SDimitry Andric // Unreachable because CodeCoverage.cpp should terminate with an error 151*0b57cec5SDimitry Andric // before we get here. 152*0b57cec5SDimitry Andric llvm_unreachable("Lcov format is not supported!"); 153*0b57cec5SDimitry Andric } 154*0b57cec5SDimitry Andric llvm_unreachable("Unknown coverage output format!"); 155*0b57cec5SDimitry Andric } 156*0b57cec5SDimitry Andric 157*0b57cec5SDimitry Andric std::string SourceCoverageView::getSourceName() const { 158*0b57cec5SDimitry Andric SmallString<128> SourceText(SourceName); 159*0b57cec5SDimitry Andric sys::path::remove_dots(SourceText, /*remove_dot_dots=*/true); 160*0b57cec5SDimitry Andric sys::path::native(SourceText); 161*0b57cec5SDimitry Andric return SourceText.str(); 162*0b57cec5SDimitry Andric } 163*0b57cec5SDimitry Andric 164*0b57cec5SDimitry Andric void SourceCoverageView::addExpansion( 165*0b57cec5SDimitry Andric const CounterMappingRegion &Region, 166*0b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> View) { 167*0b57cec5SDimitry Andric ExpansionSubViews.emplace_back(Region, std::move(View)); 168*0b57cec5SDimitry Andric } 169*0b57cec5SDimitry Andric 170*0b57cec5SDimitry Andric void SourceCoverageView::addInstantiation( 171*0b57cec5SDimitry Andric StringRef FunctionName, unsigned Line, 172*0b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> View) { 173*0b57cec5SDimitry Andric InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View)); 174*0b57cec5SDimitry Andric } 175*0b57cec5SDimitry Andric 176*0b57cec5SDimitry Andric void SourceCoverageView::print(raw_ostream &OS, bool WholeFile, 177*0b57cec5SDimitry Andric bool ShowSourceName, bool ShowTitle, 178*0b57cec5SDimitry Andric unsigned ViewDepth) { 179*0b57cec5SDimitry Andric if (ShowTitle) 180*0b57cec5SDimitry Andric renderTitle(OS, "Coverage Report"); 181*0b57cec5SDimitry Andric 182*0b57cec5SDimitry Andric renderViewHeader(OS); 183*0b57cec5SDimitry Andric 184*0b57cec5SDimitry Andric if (ShowSourceName) 185*0b57cec5SDimitry Andric renderSourceName(OS, WholeFile); 186*0b57cec5SDimitry Andric 187*0b57cec5SDimitry Andric renderTableHeader(OS, (ViewDepth > 0) ? 0 : getFirstUncoveredLineNo(), 188*0b57cec5SDimitry Andric ViewDepth); 189*0b57cec5SDimitry Andric 190*0b57cec5SDimitry Andric // We need the expansions and instantiations sorted so we can go through them 191*0b57cec5SDimitry Andric // while we iterate lines. 192*0b57cec5SDimitry Andric llvm::stable_sort(ExpansionSubViews); 193*0b57cec5SDimitry Andric llvm::stable_sort(InstantiationSubViews); 194*0b57cec5SDimitry Andric auto NextESV = ExpansionSubViews.begin(); 195*0b57cec5SDimitry Andric auto EndESV = ExpansionSubViews.end(); 196*0b57cec5SDimitry Andric auto NextISV = InstantiationSubViews.begin(); 197*0b57cec5SDimitry Andric auto EndISV = InstantiationSubViews.end(); 198*0b57cec5SDimitry Andric 199*0b57cec5SDimitry Andric // Get the coverage information for the file. 200*0b57cec5SDimitry Andric auto StartSegment = CoverageInfo.begin(); 201*0b57cec5SDimitry Andric auto EndSegment = CoverageInfo.end(); 202*0b57cec5SDimitry Andric LineCoverageIterator LCI{CoverageInfo, 1}; 203*0b57cec5SDimitry Andric LineCoverageIterator LCIEnd = LCI.getEnd(); 204*0b57cec5SDimitry Andric 205*0b57cec5SDimitry Andric unsigned FirstLine = StartSegment != EndSegment ? StartSegment->Line : 0; 206*0b57cec5SDimitry Andric for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof(); 207*0b57cec5SDimitry Andric ++LI, ++LCI) { 208*0b57cec5SDimitry Andric // If we aren't rendering the whole file, we need to filter out the prologue 209*0b57cec5SDimitry Andric // and epilogue. 210*0b57cec5SDimitry Andric if (!WholeFile) { 211*0b57cec5SDimitry Andric if (LCI == LCIEnd) 212*0b57cec5SDimitry Andric break; 213*0b57cec5SDimitry Andric else if (LI.line_number() < FirstLine) 214*0b57cec5SDimitry Andric continue; 215*0b57cec5SDimitry Andric } 216*0b57cec5SDimitry Andric 217*0b57cec5SDimitry Andric renderLinePrefix(OS, ViewDepth); 218*0b57cec5SDimitry Andric if (getOptions().ShowLineNumbers) 219*0b57cec5SDimitry Andric renderLineNumberColumn(OS, LI.line_number()); 220*0b57cec5SDimitry Andric 221*0b57cec5SDimitry Andric if (getOptions().ShowLineStats) 222*0b57cec5SDimitry Andric renderLineCoverageColumn(OS, *LCI); 223*0b57cec5SDimitry Andric 224*0b57cec5SDimitry Andric // If there are expansion subviews, we want to highlight the first one. 225*0b57cec5SDimitry Andric unsigned ExpansionColumn = 0; 226*0b57cec5SDimitry Andric if (NextESV != EndESV && NextESV->getLine() == LI.line_number() && 227*0b57cec5SDimitry Andric getOptions().Colors) 228*0b57cec5SDimitry Andric ExpansionColumn = NextESV->getStartCol(); 229*0b57cec5SDimitry Andric 230*0b57cec5SDimitry Andric // Display the source code for the current line. 231*0b57cec5SDimitry Andric renderLine(OS, {*LI, LI.line_number()}, *LCI, ExpansionColumn, ViewDepth); 232*0b57cec5SDimitry Andric 233*0b57cec5SDimitry Andric // Show the region markers. 234*0b57cec5SDimitry Andric if (shouldRenderRegionMarkers(*LCI)) 235*0b57cec5SDimitry Andric renderRegionMarkers(OS, *LCI, ViewDepth); 236*0b57cec5SDimitry Andric 237*0b57cec5SDimitry Andric // Show the expansions and instantiations for this line. 238*0b57cec5SDimitry Andric bool RenderedSubView = false; 239*0b57cec5SDimitry Andric for (; NextESV != EndESV && NextESV->getLine() == LI.line_number(); 240*0b57cec5SDimitry Andric ++NextESV) { 241*0b57cec5SDimitry Andric renderViewDivider(OS, ViewDepth + 1); 242*0b57cec5SDimitry Andric 243*0b57cec5SDimitry Andric // Re-render the current line and highlight the expansion range for 244*0b57cec5SDimitry Andric // this subview. 245*0b57cec5SDimitry Andric if (RenderedSubView) { 246*0b57cec5SDimitry Andric ExpansionColumn = NextESV->getStartCol(); 247*0b57cec5SDimitry Andric renderExpansionSite(OS, {*LI, LI.line_number()}, *LCI, ExpansionColumn, 248*0b57cec5SDimitry Andric ViewDepth); 249*0b57cec5SDimitry Andric renderViewDivider(OS, ViewDepth + 1); 250*0b57cec5SDimitry Andric } 251*0b57cec5SDimitry Andric 252*0b57cec5SDimitry Andric renderExpansionView(OS, *NextESV, ViewDepth + 1); 253*0b57cec5SDimitry Andric RenderedSubView = true; 254*0b57cec5SDimitry Andric } 255*0b57cec5SDimitry Andric for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) { 256*0b57cec5SDimitry Andric renderViewDivider(OS, ViewDepth + 1); 257*0b57cec5SDimitry Andric renderInstantiationView(OS, *NextISV, ViewDepth + 1); 258*0b57cec5SDimitry Andric RenderedSubView = true; 259*0b57cec5SDimitry Andric } 260*0b57cec5SDimitry Andric if (RenderedSubView) 261*0b57cec5SDimitry Andric renderViewDivider(OS, ViewDepth + 1); 262*0b57cec5SDimitry Andric renderLineSuffix(OS, ViewDepth); 263*0b57cec5SDimitry Andric } 264*0b57cec5SDimitry Andric 265*0b57cec5SDimitry Andric renderViewFooter(OS); 266*0b57cec5SDimitry Andric } 267