1 //===- SourceCoverageView.cpp - Code coverage view for source code --------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This class implements rendering for code coverage of source code.
12 //===----------------------------------------------------------------------===//
14 #include "SourceCoverageView.h"
15 #include "llvm/ADT/Optional.h"
16 #include "llvm/ADT/SmallString.h"
17 #include "llvm/Support/LineIterator.h"
21 void SourceCoverageView::renderLine(
22 raw_ostream
&OS
, StringRef Line
, int64_t LineNumber
,
23 const coverage::CoverageSegment
*WrappedSegment
,
24 ArrayRef
<const coverage::CoverageSegment
*> Segments
,
25 unsigned ExpansionCol
) {
26 Optional
<raw_ostream::Colors
> Highlight
;
27 SmallVector
<std::pair
<unsigned, unsigned>, 2> HighlightedRanges
;
29 // The first segment overlaps from a previous line, so we treat it specially.
30 if (WrappedSegment
&& WrappedSegment
->HasCount
&& WrappedSegment
->Count
== 0)
31 Highlight
= raw_ostream::RED
;
33 // Output each segment of the line, possibly highlighted.
35 for (const auto *S
: Segments
) {
36 unsigned End
= std::min(S
->Col
, static_cast<unsigned>(Line
.size()) + 1);
37 colored_ostream(OS
, Highlight
? *Highlight
: raw_ostream::SAVEDCOLOR
,
38 Options
.Colors
&& Highlight
, /*Bold=*/false, /*BG=*/true)
39 << Line
.substr(Col
- 1, End
- Col
);
40 if (Options
.Debug
&& Highlight
)
41 HighlightedRanges
.push_back(std::make_pair(Col
, End
));
43 if (Col
== ExpansionCol
)
44 Highlight
= raw_ostream::CYAN
;
45 else if (S
->HasCount
&& S
->Count
== 0)
46 Highlight
= raw_ostream::RED
;
51 // Show the rest of the line
52 colored_ostream(OS
, Highlight
? *Highlight
: raw_ostream::SAVEDCOLOR
,
53 Options
.Colors
&& Highlight
, /*Bold=*/false, /*BG=*/true)
54 << Line
.substr(Col
- 1, Line
.size() - Col
+ 1);
58 for (const auto &Range
: HighlightedRanges
)
59 errs() << "Highlighted line " << LineNumber
<< ", " << Range
.first
60 << " -> " << Range
.second
<< "\n";
62 errs() << "Highlighted line " << LineNumber
<< ", " << Col
<< " -> ?\n";
66 void SourceCoverageView::renderIndent(raw_ostream
&OS
, unsigned Level
) {
67 for (unsigned I
= 0; I
< Level
; ++I
)
71 void SourceCoverageView::renderViewDivider(unsigned Level
, unsigned Length
,
73 assert(Level
!= 0 && "Cannot render divider at top level");
74 renderIndent(OS
, Level
- 1);
76 for (unsigned I
= 0; I
< Length
; ++I
)
81 SourceCoverageView::renderLineCoverageColumn(raw_ostream
&OS
,
82 const LineCoverageInfo
&Line
) {
83 if (!Line
.isMapped()) {
84 OS
.indent(LineCoverageColumnWidth
) << '|';
87 SmallString
<32> Buffer
;
88 raw_svector_ostream
BufferOS(Buffer
);
89 BufferOS
<< Line
.ExecutionCount
;
90 auto Str
= BufferOS
.str();
92 Str
= Str
.substr(0, std::min(Str
.size(), (size_t)LineCoverageColumnWidth
));
94 OS
.indent(LineCoverageColumnWidth
- Str
.size());
95 colored_ostream(OS
, raw_ostream::MAGENTA
,
96 Line
.hasMultipleRegions() && Options
.Colors
)
101 void SourceCoverageView::renderLineNumberColumn(raw_ostream
&OS
,
103 SmallString
<32> Buffer
;
104 raw_svector_ostream
BufferOS(Buffer
);
106 auto Str
= BufferOS
.str();
107 // Trim and align to the right
108 Str
= Str
.substr(0, std::min(Str
.size(), (size_t)LineNumberColumnWidth
));
109 OS
.indent(LineNumberColumnWidth
- Str
.size()) << Str
<< '|';
112 void SourceCoverageView::renderRegionMarkers(
113 raw_ostream
&OS
, ArrayRef
<const coverage::CoverageSegment
*> Segments
) {
114 SmallString
<32> Buffer
;
115 raw_svector_ostream
BufferOS(Buffer
);
117 unsigned PrevColumn
= 1;
118 for (const auto *S
: Segments
) {
119 if (!S
->IsRegionEntry
)
121 // Skip to the new region
122 if (S
->Col
> PrevColumn
)
123 OS
.indent(S
->Col
- PrevColumn
);
124 PrevColumn
= S
->Col
+ 1;
125 BufferOS
<< S
->Count
;
126 StringRef Str
= BufferOS
.str();
127 // Trim the execution count
128 Str
= Str
.substr(0, std::min(Str
.size(), (size_t)7));
129 PrevColumn
+= Str
.size();
136 for (const auto *S
: Segments
)
137 errs() << "Marker at " << S
->Line
<< ":" << S
->Col
<< " = " << S
->Count
138 << (S
->IsRegionEntry
? "\n" : " (pop)\n");
141 void SourceCoverageView::render(raw_ostream
&OS
, bool WholeFile
,
142 unsigned IndentLevel
) {
143 // The width of the leading columns
144 unsigned CombinedColumnWidth
=
145 (Options
.ShowLineStats
? LineCoverageColumnWidth
+ 1 : 0) +
146 (Options
.ShowLineNumbers
? LineNumberColumnWidth
+ 1 : 0);
147 // The width of the line that is used to divide between the view and the
149 unsigned DividerWidth
= CombinedColumnWidth
+ 4;
151 // We need the expansions and instantiations sorted so we can go through them
152 // while we iterate lines.
153 std::sort(ExpansionSubViews
.begin(), ExpansionSubViews
.end());
154 std::sort(InstantiationSubViews
.begin(), InstantiationSubViews
.end());
155 auto NextESV
= ExpansionSubViews
.begin();
156 auto EndESV
= ExpansionSubViews
.end();
157 auto NextISV
= InstantiationSubViews
.begin();
158 auto EndISV
= InstantiationSubViews
.end();
160 // Get the coverage information for the file.
161 auto NextSegment
= CoverageInfo
.begin();
162 auto EndSegment
= CoverageInfo
.end();
164 unsigned FirstLine
= NextSegment
!= EndSegment
? NextSegment
->Line
: 0;
165 const coverage::CoverageSegment
*WrappedSegment
= nullptr;
166 SmallVector
<const coverage::CoverageSegment
*, 8> LineSegments
;
167 for (line_iterator
LI(File
, /*SkipBlanks=*/false); !LI
.is_at_eof(); ++LI
) {
168 // If we aren't rendering the whole file, we need to filter out the prologue
171 if (NextSegment
== EndSegment
)
173 else if (LI
.line_number() < FirstLine
)
177 // Collect the coverage information relevant to this line.
178 if (LineSegments
.size())
179 WrappedSegment
= LineSegments
.back();
180 LineSegments
.clear();
181 while (NextSegment
!= EndSegment
&& NextSegment
->Line
== LI
.line_number())
182 LineSegments
.push_back(&*NextSegment
++);
184 // Calculate a count to be for the line as a whole.
185 LineCoverageInfo LineCount
;
186 if (WrappedSegment
&& WrappedSegment
->HasCount
)
187 LineCount
.addRegionCount(WrappedSegment
->Count
);
188 for (const auto *S
: LineSegments
)
189 if (S
->HasCount
&& S
->IsRegionEntry
)
190 LineCount
.addRegionStartCount(S
->Count
);
192 // Render the line prefix.
193 renderIndent(OS
, IndentLevel
);
194 if (Options
.ShowLineStats
)
195 renderLineCoverageColumn(OS
, LineCount
);
196 if (Options
.ShowLineNumbers
)
197 renderLineNumberColumn(OS
, LI
.line_number());
199 // If there are expansion subviews, we want to highlight the first one.
200 unsigned ExpansionColumn
= 0;
201 if (NextESV
!= EndESV
&& NextESV
->getLine() == LI
.line_number() &&
203 ExpansionColumn
= NextESV
->getStartCol();
205 // Display the source code for the current line.
206 renderLine(OS
, *LI
, LI
.line_number(), WrappedSegment
, LineSegments
,
209 // Show the region markers.
210 if (Options
.ShowRegionMarkers
&& (!Options
.ShowLineStatsOrRegionMarkers
||
211 LineCount
.hasMultipleRegions()) &&
212 !LineSegments
.empty()) {
213 renderIndent(OS
, IndentLevel
);
214 OS
.indent(CombinedColumnWidth
);
215 renderRegionMarkers(OS
, LineSegments
);
218 // Show the expansions and instantiations for this line.
219 unsigned NestedIndent
= IndentLevel
+ 1;
220 bool RenderedSubView
= false;
221 for (; NextESV
!= EndESV
&& NextESV
->getLine() == LI
.line_number();
223 renderViewDivider(NestedIndent
, DividerWidth
, OS
);
225 if (RenderedSubView
) {
226 // Re-render the current line and highlight the expansion range for
228 ExpansionColumn
= NextESV
->getStartCol();
229 renderIndent(OS
, IndentLevel
);
230 OS
.indent(CombinedColumnWidth
+ (IndentLevel
== 0 ? 0 : 1));
231 renderLine(OS
, *LI
, LI
.line_number(), WrappedSegment
, LineSegments
,
233 renderViewDivider(NestedIndent
, DividerWidth
, OS
);
236 // Render the child subview
238 errs() << "Expansion at line " << NextESV
->getLine() << ", "
239 << NextESV
->getStartCol() << " -> " << NextESV
->getEndCol()
241 NextESV
->View
->render(OS
, false, NestedIndent
);
242 RenderedSubView
= true;
244 for (; NextISV
!= EndISV
&& NextISV
->Line
== LI
.line_number(); ++NextISV
) {
245 renderViewDivider(NestedIndent
, DividerWidth
, OS
);
247 renderIndent(OS
, NestedIndent
);
249 Options
.colored_ostream(OS
, raw_ostream::CYAN
) << NextISV
->FunctionName
252 NextISV
->View
->render(OS
, false, NestedIndent
);
253 RenderedSubView
= true;
255 if (RenderedSubView
) {
256 renderViewDivider(NestedIndent
, DividerWidth
, OS
);