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/SmallString.h"
16 #include "llvm/Support/LineIterator.h"
20 void SourceCoverageView::renderLine(raw_ostream &OS, StringRef Line,
21 ArrayRef<HighlightRange> Ranges) {
29 unsigned PrevColumnStart = 0;
31 for (const auto &Range : Ranges) {
32 if (PrevColumnStart == Range.ColumnStart)
35 // Show the unhighlighted part
36 unsigned ColumnStart = PrevColumnStart = Range.ColumnStart;
37 OS << Line.substr(Start - 1, ColumnStart - Start);
39 // Show the highlighted part
40 auto Color = Range.Kind == HighlightRange::NotCovered ? raw_ostream::RED
42 OS.changeColor(Color, false, true);
43 unsigned ColumnEnd = std::min(Range.ColumnEnd, (unsigned)Line.size() + 1);
44 OS << Line.substr(ColumnStart - 1, ColumnEnd - ColumnStart);
49 // Show the rest of the line
50 OS << Line.substr(Start - 1, Line.size() - Start + 1);
54 for (const auto &Range : Ranges) {
55 errs() << "Highlighted line " << Range.Line << ", " << Range.ColumnStart
57 if (Range.ColumnEnd == std::numeric_limits<unsigned>::max()) {
60 errs() << Range.ColumnEnd << "\n";
66 void SourceCoverageView::renderOffset(raw_ostream &OS, unsigned I) {
67 for (unsigned J = 0; J < I; ++J)
71 void SourceCoverageView::renderViewDivider(unsigned Offset, unsigned Length,
73 for (unsigned J = 1; J < Offset; ++J)
77 for (unsigned I = 0; I < Length; ++I)
82 SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,
83 const LineCoverageInfo &Line) {
84 if (!Line.isMapped()) {
85 OS.indent(LineCoverageColumnWidth) << '|';
88 SmallString<32> Buffer;
89 raw_svector_ostream BufferOS(Buffer);
90 BufferOS << Line.ExecutionCount;
91 auto Str = BufferOS.str();
93 Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth));
95 OS.indent(LineCoverageColumnWidth - Str.size());
96 colored_ostream(OS, raw_ostream::MAGENTA,
97 Line.hasMultipleRegions() && Options.Colors)
102 void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
104 SmallString<32> Buffer;
105 raw_svector_ostream BufferOS(Buffer);
107 auto Str = BufferOS.str();
108 // Trim and align to the right
109 Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
110 OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
113 void SourceCoverageView::renderRegionMarkers(raw_ostream &OS,
114 ArrayRef<RegionMarker> Regions) {
115 SmallString<32> Buffer;
116 raw_svector_ostream BufferOS(Buffer);
118 unsigned PrevColumn = 1;
119 for (const auto &Region : Regions) {
120 // Skip to the new region
121 if (Region.Column > PrevColumn)
122 OS.indent(Region.Column - PrevColumn);
123 PrevColumn = Region.Column + 1;
124 BufferOS << Region.ExecutionCount;
125 StringRef Str = BufferOS.str();
126 // Trim the execution count
127 Str = Str.substr(0, std::min(Str.size(), (size_t)7));
128 PrevColumn += Str.size();
135 for (const auto &Region : Regions) {
136 errs() << "Marker at " << Region.Line << ":" << Region.Column << " = "
137 << Region.ExecutionCount << "\n";
142 /// \brief Insert a new highlighting range into the line's highlighting ranges
143 /// Return line's new highlighting ranges in result.
144 static void insertHighlightRange(
145 ArrayRef<SourceCoverageView::HighlightRange> Ranges,
146 SourceCoverageView::HighlightRange RangeToInsert,
147 SmallVectorImpl<SourceCoverageView::HighlightRange> &Result) {
150 auto E = Ranges.size();
152 if (RangeToInsert.ColumnStart < Ranges[I].ColumnEnd) {
153 const auto &Range = Ranges[I];
154 bool NextRangeContainsInserted = false;
155 // If the next range starts before the inserted range, move the end of the
156 // next range to the start of the inserted range.
157 if (Range.ColumnStart < RangeToInsert.ColumnStart) {
158 if (RangeToInsert.ColumnStart != Range.ColumnStart)
159 Result.push_back(SourceCoverageView::HighlightRange(
160 Range.Line, Range.ColumnStart, RangeToInsert.ColumnStart,
162 // If the next range also ends after the inserted range, keep this range
163 // and create a new range that starts at the inserted range and ends
164 // at the next range later.
165 if (Range.ColumnEnd > RangeToInsert.ColumnEnd)
166 NextRangeContainsInserted = true;
168 if (!NextRangeContainsInserted) {
170 // Ignore ranges that are contained in inserted range
171 while (I < E && RangeToInsert.contains(Ranges[I]))
176 Result.push_back(Ranges[I]);
178 Result.push_back(RangeToInsert);
179 // If the next range starts before the inserted range end, move the start
180 // of the next range to the end of the inserted range.
181 if (I < E && Ranges[I].ColumnStart < RangeToInsert.ColumnEnd) {
182 const auto &Range = Ranges[I];
183 if (RangeToInsert.ColumnEnd != Range.ColumnEnd)
184 Result.push_back(SourceCoverageView::HighlightRange(
185 Range.Line, RangeToInsert.ColumnEnd, Range.ColumnEnd, Range.Kind));
188 // Add the remaining ranges that are located after the inserted range
190 Result.push_back(Ranges[I]);
193 void SourceCoverageView::sortChildren() {
194 for (auto &I : Children)
196 std::sort(Children.begin(), Children.end(),
197 [](const std::unique_ptr<SourceCoverageView> &LHS,
198 const std::unique_ptr<SourceCoverageView> &RHS) {
199 return LHS->ExpansionRegion < RHS->ExpansionRegion;
203 SourceCoverageView::HighlightRange
204 SourceCoverageView::getExpansionHighlightRange() const {
205 return HighlightRange(ExpansionRegion.LineStart, ExpansionRegion.ColumnStart,
206 ExpansionRegion.ColumnEnd, HighlightRange::Expanded);
209 template <typename T>
210 ArrayRef<T> gatherLineItems(size_t &CurrentIdx, const std::vector<T> &Items,
212 auto PrevIdx = CurrentIdx;
213 auto E = Items.size();
214 while (CurrentIdx < E && Items[CurrentIdx].Line == LineNo)
216 return ArrayRef<T>(Items.data() + PrevIdx, CurrentIdx - PrevIdx);
219 ArrayRef<std::unique_ptr<SourceCoverageView>>
220 gatherLineSubViews(size_t &CurrentIdx,
221 ArrayRef<std::unique_ptr<SourceCoverageView>> Items,
223 auto PrevIdx = CurrentIdx;
224 auto E = Items.size();
225 while (CurrentIdx < E &&
226 Items[CurrentIdx]->getSubViewsExpansionLine() == LineNo)
228 return Items.slice(PrevIdx, CurrentIdx - PrevIdx);
231 void SourceCoverageView::render(raw_ostream &OS, unsigned Offset) {
232 // Make sure that the children are in sorted order.
235 SmallVector<HighlightRange, 8> AdjustedLineHighlightRanges;
236 size_t CurrentChild = 0;
237 size_t CurrentHighlightRange = 0;
238 size_t CurrentRegionMarker = 0;
240 line_iterator Lines(File);
241 // Advance the line iterator to the first line.
242 while (Lines.line_number() < LineOffset)
245 // The width of the leading columns
246 unsigned CombinedColumnWidth =
247 (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
248 (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
249 // The width of the line that is used to divide between the view and the
251 unsigned DividerWidth = CombinedColumnWidth + 4;
253 for (size_t I = 0, E = LineStats.size(); I < E; ++I) {
254 unsigned LineNo = I + LineOffset;
256 // Gather the child subviews that are visible on this line.
257 auto LineSubViews = gatherLineSubViews(CurrentChild, Children, LineNo);
259 renderOffset(OS, Offset);
260 if (Options.ShowLineStats)
261 renderLineCoverageColumn(OS, LineStats[I]);
262 if (Options.ShowLineNumbers)
263 renderLineNumberColumn(OS, LineNo);
265 // Gather highlighting ranges.
266 auto LineHighlightRanges =
267 gatherLineItems(CurrentHighlightRange, HighlightRanges, LineNo);
268 auto LineRanges = LineHighlightRanges;
269 // Highlight the expansion range if there is an expansion subview on this
271 if (!LineSubViews.empty() && LineSubViews.front()->isExpansionSubView() &&
273 insertHighlightRange(LineHighlightRanges,
274 LineSubViews.front()->getExpansionHighlightRange(),
275 AdjustedLineHighlightRanges);
276 LineRanges = AdjustedLineHighlightRanges;
279 // Display the source code for the current line.
280 StringRef Line = *Lines;
281 // Check if the line is empty, as line_iterator skips blank lines.
282 if (LineNo < Lines.line_number())
284 else if (!Lines.is_at_eof())
286 renderLine(OS, Line, LineRanges);
288 // Show the region markers.
289 bool ShowMarkers = !Options.ShowLineStatsOrRegionMarkers ||
290 LineStats[I].hasMultipleRegions();
291 auto LineMarkers = gatherLineItems(CurrentRegionMarker, Markers, LineNo);
292 if (ShowMarkers && !LineMarkers.empty()) {
293 renderOffset(OS, Offset);
294 OS.indent(CombinedColumnWidth);
295 renderRegionMarkers(OS, LineMarkers);
298 // Show the line's expanded child subviews.
299 bool FirstChildExpansion = true;
300 if (LineSubViews.empty())
302 unsigned NewOffset = Offset + 1;
303 renderViewDivider(NewOffset, DividerWidth, OS);
305 for (const auto &Child : LineSubViews) {
306 // If this subview shows a function instantiation, render the function's
308 if (Child->isInstantiationSubView()) {
309 renderOffset(OS, NewOffset);
311 Options.colored_ostream(OS, raw_ostream::CYAN) << Child->FunctionName
315 if (!FirstChildExpansion) {
316 // Re-render the current line and highlight the expansion range for
319 insertHighlightRange(LineHighlightRanges,
320 Child->getExpansionHighlightRange(),
321 AdjustedLineHighlightRanges);
322 renderOffset(OS, Offset);
323 OS.indent(CombinedColumnWidth + (Offset == 0 ? 0 : 1));
324 renderLine(OS, Line, AdjustedLineHighlightRanges);
325 renderViewDivider(NewOffset, DividerWidth, OS);
328 FirstChildExpansion = false;
330 // Render the child subview
331 Child->render(OS, NewOffset);
332 renderViewDivider(NewOffset, DividerWidth, OS);
338 void SourceCoverageView::setUpVisibleRange(SourceCoverageDataManager &Data) {
339 auto CountedRegions = Data.getSourceRegions();
340 if (!CountedRegions.size())
343 unsigned Start = CountedRegions.front().LineStart, End = 0;
344 for (const auto &CR : CountedRegions) {
345 Start = std::min(Start, CR.LineStart);
346 End = std::max(End, CR.LineEnd);
349 LineStats.resize(End - Start + 1);
353 SourceCoverageView::createLineCoverageInfo(SourceCoverageDataManager &Data) {
354 auto CountedRegions = Data.getSourceRegions();
355 for (const auto &CR : CountedRegions) {
356 if (CR.Kind == coverage::CounterMappingRegion::SkippedRegion) {
357 // Reset the line stats for skipped regions.
358 for (unsigned Line = CR.LineStart; Line <= CR.LineEnd;
360 LineStats[Line - LineOffset] = LineCoverageInfo();
363 LineStats[CR.LineStart - LineOffset].addRegionStartCount(CR.ExecutionCount);
364 for (unsigned Line = CR.LineStart + 1; Line <= CR.LineEnd; ++Line)
365 LineStats[Line - LineOffset].addRegionCount(CR.ExecutionCount);
370 SourceCoverageView::createHighlightRanges(SourceCoverageDataManager &Data) {
371 auto CountedRegions = Data.getSourceRegions();
372 std::vector<bool> AlreadyHighlighted;
373 AlreadyHighlighted.resize(CountedRegions.size(), false);
375 for (size_t I = 0, S = CountedRegions.size(); I < S; ++I) {
376 const auto &CR = CountedRegions[I];
377 if (CR.Kind == coverage::CounterMappingRegion::SkippedRegion ||
378 CR.ExecutionCount != 0)
380 if (AlreadyHighlighted[I])
382 for (size_t J = 0; J < S; ++J) {
383 if (CR.contains(CountedRegions[J])) {
384 AlreadyHighlighted[J] = true;
387 if (CR.LineStart == CR.LineEnd) {
388 HighlightRanges.push_back(HighlightRange(
389 CR.LineStart, CR.ColumnStart, CR.ColumnEnd));
392 HighlightRanges.push_back(
393 HighlightRange(CR.LineStart, CR.ColumnStart,
394 std::numeric_limits<unsigned>::max()));
395 HighlightRanges.push_back(
396 HighlightRange(CR.LineEnd, 1, CR.ColumnEnd));
397 for (unsigned Line = CR.LineStart + 1; Line < CR.LineEnd;
399 HighlightRanges.push_back(
400 HighlightRange(Line, 1, std::numeric_limits<unsigned>::max()));
404 std::sort(HighlightRanges.begin(), HighlightRanges.end());
407 void SourceCoverageView::createRegionMarkers(SourceCoverageDataManager &Data) {
408 for (const auto &CR : Data.getSourceRegions())
409 if (CR.Kind != coverage::CounterMappingRegion::SkippedRegion)
411 RegionMarker(CR.LineStart, CR.ColumnStart, CR.ExecutionCount));
414 void SourceCoverageView::load(SourceCoverageDataManager &Data) {
415 setUpVisibleRange(Data);
416 if (Options.ShowLineStats)
417 createLineCoverageInfo(Data);
419 createHighlightRanges(Data);
420 if (Options.ShowRegionMarkers)
421 createRegionMarkers(Data);