1 //===- SourceCoverageView.cpp - Code coverage view for source code --------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 /// 10 /// \file This class implements rendering for code coverage of source code. 11 /// 12 //===----------------------------------------------------------------------===// 13 14 #include "SourceCoverageView.h" 15 #include "SourceCoverageViewText.h" 16 #include "llvm/ADT/SmallString.h" 17 #include "llvm/ADT/StringExtras.h" 18 #include "llvm/Support/FileSystem.h" 19 #include "llvm/Support/LineIterator.h" 20 #include "llvm/Support/Path.h" 21 22 using namespace llvm; 23 24 std::string SourceCoverageView::formatCount(uint64_t N) { 25 std::string Number = utostr(N); 26 int Len = Number.size(); 27 if (Len <= 3) 28 return Number; 29 int IntLen = Len % 3 == 0 ? 3 : Len % 3; 30 std::string Result(Number.data(), IntLen); 31 if (IntLen != 3) { 32 Result.push_back('.'); 33 Result += Number.substr(IntLen, 3 - IntLen); 34 } 35 Result.push_back(" kMGTPEZY"[(Len - 1) / 3]); 36 return Result; 37 } 38 39 /// \brief Create a file at ``Dir/ToplevelDir/@Path.Extension``. If 40 /// \p ToplevelDir is empty, its path component is skipped. 41 static Expected<std::unique_ptr<raw_ostream>> 42 createFileInDirectory(StringRef Dir, StringRef ToplevelDir, StringRef Path, 43 StringRef Extension) { 44 assert(Extension.size() && "The file extension may not be empty"); 45 46 SmallString<256> FullPath(Dir); 47 if (!ToplevelDir.empty()) 48 sys::path::append(FullPath, ToplevelDir); 49 50 auto PathBaseDir = sys::path::relative_path(sys::path::parent_path(Path)); 51 sys::path::append(FullPath, PathBaseDir); 52 53 if (auto E = sys::fs::create_directories(FullPath)) 54 return errorCodeToError(E); 55 56 auto PathFilename = (sys::path::filename(Path) + "." + Extension).str(); 57 sys::path::append(FullPath, PathFilename); 58 59 std::error_code E; 60 auto OS = llvm::make_unique<raw_fd_ostream>(FullPath, E, sys::fs::F_RW); 61 if (E) 62 return errorCodeToError(E); 63 return std::move(OS); 64 } 65 66 Expected<std::unique_ptr<raw_ostream>> 67 SourceCoverageView::createOutputStream(const CoverageViewOptions &Opts, 68 StringRef Path, StringRef Extension, 69 bool InToplevel) { 70 if (Opts.ShowOutputDirectory == "") { 71 std::error_code E; 72 auto OS = llvm::make_unique<raw_fd_ostream>("-", E, sys::fs::F_None); 73 if (E) 74 return errorCodeToError(E); 75 return std::move(OS); 76 } 77 78 return createFileInDirectory(Opts.ShowOutputDirectory, 79 InToplevel ? "" : "coverage", Path, Extension); 80 } 81 82 void SourceCoverageView::addExpansion( 83 const coverage::CounterMappingRegion &Region, 84 std::unique_ptr<SourceCoverageView> View) { 85 ExpansionSubViews.emplace_back(Region, std::move(View)); 86 } 87 88 void SourceCoverageView::addInstantiation( 89 StringRef FunctionName, unsigned Line, 90 std::unique_ptr<SourceCoverageView> View) { 91 InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View)); 92 } 93 94 std::unique_ptr<SourceCoverageView> 95 SourceCoverageView::create(StringRef SourceName, const MemoryBuffer &File, 96 const CoverageViewOptions &Options, 97 coverage::CoverageData &&CoverageInfo) { 98 switch (Options.ShowFormat) { 99 case CoverageViewOptions::OutputFormat::Text: 100 return llvm::make_unique<SourceCoverageViewText>(SourceName, File, Options, 101 std::move(CoverageInfo)); 102 } 103 } 104 105 void SourceCoverageView::print(raw_ostream &OS, bool WholeFile, 106 bool ShowSourceName, unsigned ViewDepth) { 107 if (ShowSourceName) 108 renderSourceName(OS); 109 110 // We need the expansions and instantiations sorted so we can go through them 111 // while we iterate lines. 112 std::sort(ExpansionSubViews.begin(), ExpansionSubViews.end()); 113 std::sort(InstantiationSubViews.begin(), InstantiationSubViews.end()); 114 auto NextESV = ExpansionSubViews.begin(); 115 auto EndESV = ExpansionSubViews.end(); 116 auto NextISV = InstantiationSubViews.begin(); 117 auto EndISV = InstantiationSubViews.end(); 118 119 // Get the coverage information for the file. 120 auto NextSegment = CoverageInfo.begin(); 121 auto EndSegment = CoverageInfo.end(); 122 123 unsigned FirstLine = NextSegment != EndSegment ? NextSegment->Line : 0; 124 const coverage::CoverageSegment *WrappedSegment = nullptr; 125 SmallVector<const coverage::CoverageSegment *, 8> LineSegments; 126 for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof(); ++LI) { 127 // If we aren't rendering the whole file, we need to filter out the prologue 128 // and epilogue. 129 if (!WholeFile) { 130 if (NextSegment == EndSegment) 131 break; 132 else if (LI.line_number() < FirstLine) 133 continue; 134 } 135 136 // Collect the coverage information relevant to this line. 137 if (LineSegments.size()) 138 WrappedSegment = LineSegments.back(); 139 LineSegments.clear(); 140 while (NextSegment != EndSegment && NextSegment->Line == LI.line_number()) 141 LineSegments.push_back(&*NextSegment++); 142 143 // Calculate a count to be for the line as a whole. 144 LineCoverageStats LineCount; 145 if (WrappedSegment && WrappedSegment->HasCount) 146 LineCount.addRegionCount(WrappedSegment->Count); 147 for (const auto *S : LineSegments) 148 if (S->HasCount && S->IsRegionEntry) 149 LineCount.addRegionStartCount(S->Count); 150 151 renderLinePrefix(OS, ViewDepth); 152 if (getOptions().ShowLineStats) 153 renderLineCoverageColumn(OS, LineCount); 154 if (getOptions().ShowLineNumbers) 155 renderLineNumberColumn(OS, LI.line_number()); 156 157 // If there are expansion subviews, we want to highlight the first one. 158 unsigned ExpansionColumn = 0; 159 if (NextESV != EndESV && NextESV->getLine() == LI.line_number() && 160 getOptions().Colors) 161 ExpansionColumn = NextESV->getStartCol(); 162 163 // Display the source code for the current line. 164 renderLine(OS, {*LI, LI.line_number()}, WrappedSegment, LineSegments, 165 ExpansionColumn, ViewDepth); 166 167 // Show the region markers. 168 if (getOptions().ShowRegionMarkers && 169 (!getOptions().ShowLineStatsOrRegionMarkers || 170 LineCount.hasMultipleRegions()) && 171 !LineSegments.empty()) { 172 renderRegionMarkers(OS, LineSegments, ViewDepth); 173 } 174 175 // Show the expansions and instantiations for this line. 176 bool RenderedSubView = false; 177 for (; NextESV != EndESV && NextESV->getLine() == LI.line_number(); 178 ++NextESV) { 179 renderViewDivider(OS, ViewDepth + 1); 180 181 // Re-render the current line and highlight the expansion range for 182 // this subview. 183 if (RenderedSubView) { 184 ExpansionColumn = NextESV->getStartCol(); 185 renderExpansionSite( 186 OS, *NextESV, {*LI, LI.line_number()}, WrappedSegment, LineSegments, 187 ExpansionColumn, ViewDepth); 188 renderViewDivider(OS, ViewDepth + 1); 189 } 190 191 renderExpansionView(OS, *NextESV, ViewDepth + 1); 192 RenderedSubView = true; 193 } 194 for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) { 195 renderViewDivider(OS, ViewDepth + 1); 196 renderInstantiationView(OS, *NextISV, ViewDepth + 1); 197 RenderedSubView = true; 198 } 199 if (RenderedSubView) 200 renderViewDivider(OS, ViewDepth + 1); 201 } 202 } 203