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::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(raw_ostream &OS,
113 ArrayRef<RegionMarker> Regions) {
114 SmallString<32> Buffer;
115 raw_svector_ostream BufferOS(Buffer);
117 unsigned PrevColumn = 1;
118 for (const auto &Region : Regions) {
119 // Skip to the new region
120 if (Region.Column > PrevColumn)
121 OS.indent(Region.Column - PrevColumn);
122 PrevColumn = Region.Column + 1;
123 BufferOS << Region.ExecutionCount;
124 StringRef Str = BufferOS.str();
125 // Trim the execution count
126 Str = Str.substr(0, std::min(Str.size(), (size_t)7));
127 PrevColumn += Str.size();
134 for (const auto &Region : Regions) {
135 errs() << "Marker at " << Region.Line << ":" << Region.Column << " = "
136 << Region.ExecutionCount << "\n";
141 /// \brief Insert a new highlighting range into the line's highlighting ranges
142 /// Return line's new highlighting ranges in result.
143 static void insertExpansionHighlightRange(
144 ArrayRef<SourceCoverageView::HighlightRange> Ranges,
145 unsigned Line, unsigned StartCol, unsigned EndCol,
146 SmallVectorImpl<SourceCoverageView::HighlightRange> &Result) {
147 auto RangeToInsert = SourceCoverageView::HighlightRange(
148 Line, StartCol, EndCol, SourceCoverageView::HighlightRange::Expanded);
151 auto E = Ranges.size();
153 if (RangeToInsert.ColumnStart < Ranges[I].ColumnEnd) {
154 const auto &Range = Ranges[I];
155 bool NextRangeContainsInserted = false;
156 // If the next range starts before the inserted range, move the end of the
157 // next range to the start of the inserted range.
158 if (Range.ColumnStart < RangeToInsert.ColumnStart) {
159 if (RangeToInsert.ColumnStart != Range.ColumnStart)
160 Result.push_back(SourceCoverageView::HighlightRange(
161 Range.Line, Range.ColumnStart, RangeToInsert.ColumnStart,
163 // If the next range also ends after the inserted range, keep this range
164 // and create a new range that starts at the inserted range and ends
165 // at the next range later.
166 if (Range.ColumnEnd > RangeToInsert.ColumnEnd)
167 NextRangeContainsInserted = true;
169 if (!NextRangeContainsInserted) {
171 // Ignore ranges that are contained in inserted range
172 while (I < E && RangeToInsert.contains(Ranges[I]))
177 Result.push_back(Ranges[I]);
179 Result.push_back(RangeToInsert);
180 // If the next range starts before the inserted range end, move the start
181 // of the next range to the end of the inserted range.
182 if (I < E && Ranges[I].ColumnStart < RangeToInsert.ColumnEnd) {
183 const auto &Range = Ranges[I];
184 if (RangeToInsert.ColumnEnd != Range.ColumnEnd)
185 Result.push_back(SourceCoverageView::HighlightRange(
186 Range.Line, RangeToInsert.ColumnEnd, Range.ColumnEnd, Range.Kind));
189 // Add the remaining ranges that are located after the inserted range
191 Result.push_back(Ranges[I]);
194 template <typename T>
195 ArrayRef<T> gatherLineItems(size_t &CurrentIdx, const std::vector<T> &Items,
197 auto PrevIdx = CurrentIdx;
198 auto E = Items.size();
199 while (CurrentIdx < E && Items[CurrentIdx].Line == LineNo)
201 return ArrayRef<T>(Items.data() + PrevIdx, CurrentIdx - PrevIdx);
204 void SourceCoverageView::render(raw_ostream &OS, unsigned IndentLevel) {
205 SmallVector<HighlightRange, 8> AdjustedLineHighlightRanges;
206 size_t CurrentHighlightRange = 0;
207 size_t CurrentRegionMarker = 0;
209 line_iterator Lines(File);
210 // Advance the line iterator to the first line.
211 while (Lines.line_number() < LineOffset)
214 // The width of the leading columns
215 unsigned CombinedColumnWidth =
216 (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
217 (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
218 // The width of the line that is used to divide between the view and the
220 unsigned DividerWidth = CombinedColumnWidth + 4;
222 // We need the expansions and instantiations sorted so we can go through them
223 // while we iterate lines.
224 std::sort(ExpansionSubViews.begin(), ExpansionSubViews.end());
225 std::sort(InstantiationSubViews.begin(), InstantiationSubViews.end());
226 auto NextESV = ExpansionSubViews.begin();
227 auto EndESV = ExpansionSubViews.end();
228 auto NextISV = InstantiationSubViews.begin();
229 auto EndISV = InstantiationSubViews.end();
231 for (size_t I = 0, E = LineStats.size(); I < E; ++I) {
232 unsigned LineNo = I + LineOffset;
234 renderIndent(OS, IndentLevel);
235 if (Options.ShowLineStats)
236 renderLineCoverageColumn(OS, LineStats[I]);
237 if (Options.ShowLineNumbers)
238 renderLineNumberColumn(OS, LineNo);
240 // Gather highlighting ranges.
241 auto LineHighlightRanges =
242 gatherLineItems(CurrentHighlightRange, HighlightRanges, LineNo);
243 auto LineRanges = LineHighlightRanges;
244 // Highlight the expansion range if there is an expansion subview on this
246 if (NextESV != EndESV && NextESV->getLine() == LineNo && Options.Colors) {
247 insertExpansionHighlightRange(
248 LineHighlightRanges, NextESV->getLine(), NextESV->getStartCol(),
249 NextESV->getEndCol(), AdjustedLineHighlightRanges);
250 LineRanges = AdjustedLineHighlightRanges;
253 // Display the source code for the current line.
254 StringRef Line = *Lines;
255 // Check if the line is empty, as line_iterator skips blank lines.
256 if (LineNo < Lines.line_number())
258 else if (!Lines.is_at_eof())
260 renderLine(OS, Line, LineRanges);
262 // Show the region markers.
263 bool ShowMarkers = !Options.ShowLineStatsOrRegionMarkers ||
264 LineStats[I].hasMultipleRegions();
265 auto LineMarkers = gatherLineItems(CurrentRegionMarker, Markers, LineNo);
266 if (ShowMarkers && !LineMarkers.empty()) {
267 renderIndent(OS, IndentLevel);
268 OS.indent(CombinedColumnWidth);
269 renderRegionMarkers(OS, LineMarkers);
272 // Show the expansions and instantiations for this line.
273 unsigned NestedIndent = IndentLevel + 1;
274 bool RenderedSubView = false;
275 for (; NextESV != EndESV && NextESV->getLine() == LineNo; ++NextESV) {
276 renderViewDivider(NestedIndent, DividerWidth, OS);
278 if (RenderedSubView) {
279 // Re-render the current line and highlight the expansion range for
281 insertExpansionHighlightRange(
282 LineHighlightRanges, NextESV->getLine(), NextESV->getStartCol(),
283 NextESV->getEndCol(), AdjustedLineHighlightRanges);
284 renderIndent(OS, IndentLevel);
285 OS.indent(CombinedColumnWidth + (IndentLevel == 0 ? 0 : 1));
286 renderLine(OS, Line, AdjustedLineHighlightRanges);
287 renderViewDivider(NestedIndent, DividerWidth, OS);
290 // Render the child subview
291 NextESV->View->render(OS, NestedIndent);
292 RenderedSubView = true;
294 for (; NextISV != EndISV && NextISV->Line == LineNo; ++NextISV) {
295 renderViewDivider(NestedIndent, DividerWidth, OS);
297 renderIndent(OS, NestedIndent);
299 Options.colored_ostream(OS, raw_ostream::CYAN) << NextISV->FunctionName
302 NextISV->View->render(OS, NestedIndent);
303 RenderedSubView = true;
305 if (RenderedSubView) {
306 renderViewDivider(NestedIndent, DividerWidth, OS);
312 void SourceCoverageView::setUpVisibleRange(SourceCoverageDataManager &Data) {
313 auto CountedRegions = Data.getSourceRegions();
314 if (!CountedRegions.size())
317 unsigned Start = CountedRegions.front().LineStart, End = 0;
318 for (const auto &CR : CountedRegions) {
319 Start = std::min(Start, CR.LineStart);
320 End = std::max(End, CR.LineEnd);
323 LineStats.resize(End - Start + 1);
327 SourceCoverageView::createLineCoverageInfo(SourceCoverageDataManager &Data) {
328 auto CountedRegions = Data.getSourceRegions();
329 for (const auto &CR : CountedRegions) {
330 if (CR.Kind == coverage::CounterMappingRegion::SkippedRegion) {
331 // Reset the line stats for skipped regions.
332 for (unsigned Line = CR.LineStart; Line <= CR.LineEnd;
334 LineStats[Line - LineOffset] = LineCoverageInfo();
337 LineStats[CR.LineStart - LineOffset].addRegionStartCount(CR.ExecutionCount);
338 for (unsigned Line = CR.LineStart + 1; Line <= CR.LineEnd; ++Line)
339 LineStats[Line - LineOffset].addRegionCount(CR.ExecutionCount);
344 SourceCoverageView::createHighlightRanges(SourceCoverageDataManager &Data) {
345 auto CountedRegions = Data.getSourceRegions();
346 std::vector<bool> AlreadyHighlighted;
347 AlreadyHighlighted.resize(CountedRegions.size(), false);
349 for (size_t I = 0, S = CountedRegions.size(); I < S; ++I) {
350 const auto &CR = CountedRegions[I];
351 if (CR.Kind == coverage::CounterMappingRegion::SkippedRegion ||
352 CR.ExecutionCount != 0)
354 if (AlreadyHighlighted[I])
356 for (size_t J = 0; J < S; ++J) {
357 if (CR.contains(CountedRegions[J])) {
358 AlreadyHighlighted[J] = true;
361 if (CR.LineStart == CR.LineEnd) {
362 HighlightRanges.push_back(HighlightRange(
363 CR.LineStart, CR.ColumnStart, CR.ColumnEnd));
366 HighlightRanges.push_back(
367 HighlightRange(CR.LineStart, CR.ColumnStart,
368 std::numeric_limits<unsigned>::max()));
369 HighlightRanges.push_back(
370 HighlightRange(CR.LineEnd, 1, CR.ColumnEnd));
371 for (unsigned Line = CR.LineStart + 1; Line < CR.LineEnd;
373 HighlightRanges.push_back(
374 HighlightRange(Line, 1, std::numeric_limits<unsigned>::max()));
378 std::sort(HighlightRanges.begin(), HighlightRanges.end());
381 void SourceCoverageView::createRegionMarkers(SourceCoverageDataManager &Data) {
382 for (const auto &CR : Data.getSourceRegions())
383 if (CR.Kind != coverage::CounterMappingRegion::SkippedRegion)
385 RegionMarker(CR.LineStart, CR.ColumnStart, CR.ExecutionCount));
388 void SourceCoverageView::load(SourceCoverageDataManager &Data) {
389 setUpVisibleRange(Data);
390 if (Options.ShowLineStats)
391 createLineCoverageInfo(Data);
393 createHighlightRanges(Data);
394 if (Options.ShowRegionMarkers)
395 createRegionMarkers(Data);