xref: /llvm-project/llvm/tools/llvm-cov/SourceCoverageView.cpp (revision 635c83c1b4ec789f51d8bcb49bbbec5f127d1a69)
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/LineIterator.h"
19 
20 using namespace llvm;
21 
22 std::string SourceCoverageView::formatCount(uint64_t N) {
23   std::string Number = utostr(N);
24   int Len = Number.size();
25   if (Len <= 3)
26     return Number;
27   int IntLen = Len % 3 == 0 ? 3 : Len % 3;
28   std::string Result(Number.data(), IntLen);
29   if (IntLen != 3) {
30     Result.push_back('.');
31     Result += Number.substr(IntLen, 3 - IntLen);
32   }
33   Result.push_back(" kMGTPEZY"[(Len - 1) / 3]);
34   return Result;
35 }
36 
37 void SourceCoverageView::addExpansion(
38     const coverage::CounterMappingRegion &Region,
39     std::unique_ptr<SourceCoverageView> View) {
40   ExpansionSubViews.emplace_back(Region, std::move(View));
41 }
42 
43 void SourceCoverageView::addInstantiation(
44     StringRef FunctionName, unsigned Line,
45     std::unique_ptr<SourceCoverageView> View) {
46   InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View));
47 }
48 
49 std::unique_ptr<SourceCoverageView>
50 SourceCoverageView::create(StringRef SourceName, const MemoryBuffer &File,
51                            const CoverageViewOptions &Options,
52                            coverage::CoverageData &&CoverageInfo) {
53   switch (Options.ShowFormat) {
54   case CoverageViewOptions::OutputFormat::Text:
55     return llvm::make_unique<SourceCoverageViewText>(SourceName, File, Options,
56                                                      std::move(CoverageInfo));
57   }
58 }
59 
60 void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
61                                bool ShowSourceName, unsigned ViewDepth) {
62   if (ShowSourceName)
63     renderSourceName(OS);
64 
65   // We need the expansions and instantiations sorted so we can go through them
66   // while we iterate lines.
67   std::sort(ExpansionSubViews.begin(), ExpansionSubViews.end());
68   std::sort(InstantiationSubViews.begin(), InstantiationSubViews.end());
69   auto NextESV = ExpansionSubViews.begin();
70   auto EndESV = ExpansionSubViews.end();
71   auto NextISV = InstantiationSubViews.begin();
72   auto EndISV = InstantiationSubViews.end();
73 
74   // Get the coverage information for the file.
75   auto NextSegment = CoverageInfo.begin();
76   auto EndSegment = CoverageInfo.end();
77 
78   unsigned FirstLine = NextSegment != EndSegment ? NextSegment->Line : 0;
79   const coverage::CoverageSegment *WrappedSegment = nullptr;
80   SmallVector<const coverage::CoverageSegment *, 8> LineSegments;
81   for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof(); ++LI) {
82     // If we aren't rendering the whole file, we need to filter out the prologue
83     // and epilogue.
84     if (!WholeFile) {
85       if (NextSegment == EndSegment)
86         break;
87       else if (LI.line_number() < FirstLine)
88         continue;
89     }
90 
91     // Collect the coverage information relevant to this line.
92     if (LineSegments.size())
93       WrappedSegment = LineSegments.back();
94     LineSegments.clear();
95     while (NextSegment != EndSegment && NextSegment->Line == LI.line_number())
96       LineSegments.push_back(&*NextSegment++);
97 
98     // Calculate a count to be for the line as a whole.
99     LineCoverageStats LineCount;
100     if (WrappedSegment && WrappedSegment->HasCount)
101       LineCount.addRegionCount(WrappedSegment->Count);
102     for (const auto *S : LineSegments)
103       if (S->HasCount && S->IsRegionEntry)
104         LineCount.addRegionStartCount(S->Count);
105 
106     renderLinePrefix(OS, ViewDepth);
107     if (getOptions().ShowLineStats)
108       renderLineCoverageColumn(OS, LineCount);
109     if (getOptions().ShowLineNumbers)
110       renderLineNumberColumn(OS, LI.line_number());
111 
112     // If there are expansion subviews, we want to highlight the first one.
113     unsigned ExpansionColumn = 0;
114     if (NextESV != EndESV && NextESV->getLine() == LI.line_number() &&
115         getOptions().Colors)
116       ExpansionColumn = NextESV->getStartCol();
117 
118     // Display the source code for the current line.
119     renderLine(OS, {*LI, LI.line_number()}, WrappedSegment, LineSegments,
120                ExpansionColumn, ViewDepth);
121 
122     // Show the region markers.
123     if (getOptions().ShowRegionMarkers &&
124         (!getOptions().ShowLineStatsOrRegionMarkers ||
125          LineCount.hasMultipleRegions()) &&
126         !LineSegments.empty()) {
127       renderRegionMarkers(OS, LineSegments, ViewDepth);
128     }
129 
130     // Show the expansions and instantiations for this line.
131     bool RenderedSubView = false;
132     for (; NextESV != EndESV && NextESV->getLine() == LI.line_number();
133          ++NextESV) {
134       renderViewDivider(OS, ViewDepth + 1);
135 
136       // Re-render the current line and highlight the expansion range for
137       // this subview.
138       if (RenderedSubView) {
139         ExpansionColumn = NextESV->getStartCol();
140         renderExpansionSite(
141             OS, *NextESV, {*LI, LI.line_number()}, WrappedSegment, LineSegments,
142             ExpansionColumn, ViewDepth);
143         renderViewDivider(OS, ViewDepth + 1);
144       }
145 
146       renderExpansionView(OS, *NextESV, ViewDepth + 1);
147       RenderedSubView = true;
148     }
149     for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) {
150       renderViewDivider(OS, ViewDepth + 1);
151       renderInstantiationView(OS, *NextISV, ViewDepth + 1);
152       RenderedSubView = true;
153     }
154     if (RenderedSubView)
155       renderViewDivider(OS, ViewDepth + 1);
156   }
157 }
158