llvm-cov: add code coverage tool that's based on coverage mapping format and clang...
[oota-llvm.git] / tools / llvm-cov / SourceCoverageView.cpp
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 // This class implements rendering for code coverage of source code.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "SourceCoverageView.h"
15 #include "llvm/ADT/SmallString.h"
16 #include "llvm/Support/LineIterator.h"
17
18 using namespace llvm;
19
20 void SourceCoverageView::renderLine(raw_ostream &OS, StringRef Line,
21                                     ArrayRef<HighlightRange> Ranges) {
22   if (Ranges.empty()) {
23     OS << Line << "\n";
24     return;
25   }
26   if (Line.empty())
27     Line = " ";
28
29   unsigned PrevColumnStart = 0;
30   unsigned Start = 1;
31   for (const auto &Range : Ranges) {
32     if (PrevColumnStart == Range.ColumnStart)
33       continue;
34
35     // Show the unhighlighted part
36     unsigned ColumnStart = PrevColumnStart = Range.ColumnStart;
37     OS << Line.substr(Start - 1, ColumnStart - Start);
38
39     // Show the highlighted part
40     auto Color = Range.Kind == HighlightRange::NotCovered ? raw_ostream::RED
41                                                           : raw_ostream::CYAN;
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);
45     Start = ColumnEnd;
46     OS.resetColor();
47   }
48
49   // Show the rest of the line
50   OS << Line.substr(Start - 1, Line.size() - Start + 1);
51   OS << "\n";
52 }
53
54 void SourceCoverageView::renderOffset(raw_ostream &OS, unsigned I) {
55   for (unsigned J = 0; J < I; ++J)
56     OS << "  |";
57 }
58
59 void SourceCoverageView::renderViewDivider(unsigned Offset, unsigned Length,
60                                            raw_ostream &OS) {
61   for (unsigned J = 1; J < Offset; ++J)
62     OS << "  |";
63   if (Offset != 0)
64     OS.indent(2);
65   for (unsigned I = 0; I < Length; ++I)
66     OS << "-";
67 }
68
69 void
70 SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,
71                                              const LineCoverageInfo &Line) {
72   if (!Line.isMapped()) {
73     OS.indent(LineCoverageColumnWidth) << '|';
74     return;
75   }
76   SmallString<32> Buffer;
77   raw_svector_ostream BufferOS(Buffer);
78   BufferOS << Line.ExecutionCount;
79   auto Str = BufferOS.str();
80   // Trim
81   Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth));
82   // Align to the right
83   OS.indent(LineCoverageColumnWidth - Str.size());
84   colored_ostream(OS, raw_ostream::MAGENTA,
85                   Line.hasMultipleRegions() && Options.Colors)
86       << Str;
87   OS << '|';
88 }
89
90 void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
91                                                 unsigned LineNo) {
92   SmallString<32> Buffer;
93   raw_svector_ostream BufferOS(Buffer);
94   BufferOS << LineNo;
95   auto Str = BufferOS.str();
96   // Trim and align to the right
97   Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
98   OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
99 }
100
101 void SourceCoverageView::renderRegionMarkers(raw_ostream &OS,
102                                              ArrayRef<RegionMarker> Regions) {
103   SmallString<32> Buffer;
104   raw_svector_ostream BufferOS(Buffer);
105
106   unsigned PrevColumn = 1;
107   for (const auto &Region : Regions) {
108     // Skip to the new region
109     if (Region.Column > PrevColumn)
110       OS.indent(Region.Column - PrevColumn);
111     PrevColumn = Region.Column + 1;
112     BufferOS << Region.ExecutionCount;
113     StringRef Str = BufferOS.str();
114     // Trim the execution count
115     Str = Str.substr(0, std::min(Str.size(), (size_t)7));
116     PrevColumn += Str.size();
117     OS << '^' << Str;
118     Buffer.clear();
119   }
120   OS << "\n";
121 }
122
123 /// \brief Insert a new highlighting range into the line's highlighting ranges
124 /// Return line's new highlighting ranges in result.
125 static void insertHighlightRange(
126     ArrayRef<SourceCoverageView::HighlightRange> Ranges,
127     SourceCoverageView::HighlightRange RangeToInsert,
128     SmallVectorImpl<SourceCoverageView::HighlightRange> &Result) {
129   Result.clear();
130   size_t I = 0;
131   auto E = Ranges.size();
132   for (; I < E; ++I) {
133     if (RangeToInsert.ColumnStart < Ranges[I].ColumnEnd) {
134       const auto &Range = Ranges[I];
135       bool NextRangeContainsInserted = false;
136       // If the next range starts before the inserted range, move the end of the
137       // next range to the start of the inserted range.
138       if (Range.ColumnStart < RangeToInsert.ColumnStart) {
139         if (RangeToInsert.ColumnStart != Range.ColumnStart)
140           Result.push_back(SourceCoverageView::HighlightRange(
141               Range.Line, Range.ColumnStart, RangeToInsert.ColumnStart,
142               Range.Kind));
143         // If the next range also ends after the inserted range, keep this range
144         // and create a new range that starts at the inserted range and ends
145         // at the next range later.
146         if (Range.ColumnEnd > RangeToInsert.ColumnEnd)
147           NextRangeContainsInserted = true;
148       }
149       if (!NextRangeContainsInserted) {
150         ++I;
151         // Ignore ranges that are contained in inserted range
152         while (I < E && RangeToInsert.contains(Ranges[I]))
153           ++I;
154       }
155       break;
156     }
157     Result.push_back(Ranges[I]);
158   }
159   Result.push_back(RangeToInsert);
160   // If the next range starts before the inserted range end, move the start
161   // of the next range to the end of the inserted range.
162   if (I < E && Ranges[I].ColumnStart < RangeToInsert.ColumnEnd) {
163     const auto &Range = Ranges[I];
164     if (RangeToInsert.ColumnEnd != Range.ColumnEnd)
165       Result.push_back(SourceCoverageView::HighlightRange(
166           Range.Line, RangeToInsert.ColumnEnd, Range.ColumnEnd, Range.Kind));
167     ++I;
168   }
169   // Add the remaining ranges that are located after the inserted range
170   for (; I < E; ++I)
171     Result.push_back(Ranges[I]);
172 }
173
174 void SourceCoverageView::sortChildren() {
175   for (auto &I : Children)
176     I->sortChildren();
177   std::sort(Children.begin(), Children.end(),
178             [](const std::unique_ptr<SourceCoverageView> &LHS,
179                const std::unique_ptr<SourceCoverageView> &RHS) {
180     return LHS->ExpansionRegion < RHS->ExpansionRegion;
181   });
182 }
183
184 SourceCoverageView::HighlightRange
185 SourceCoverageView::getExpansionHighlightRange() const {
186   return HighlightRange(ExpansionRegion.LineStart, ExpansionRegion.ColumnStart,
187                         ExpansionRegion.ColumnEnd, HighlightRange::Expanded);
188 }
189
190 template <typename T>
191 ArrayRef<T> gatherLineItems(size_t &CurrentIdx, const std::vector<T> &Items,
192                             unsigned LineNo) {
193   auto PrevIdx = CurrentIdx;
194   auto E = Items.size();
195   while (CurrentIdx < E && Items[CurrentIdx].Line == LineNo)
196     ++CurrentIdx;
197   return ArrayRef<T>(Items.data() + PrevIdx, CurrentIdx - PrevIdx);
198 }
199
200 ArrayRef<std::unique_ptr<SourceCoverageView>>
201 gatherLineSubViews(size_t &CurrentIdx,
202                    ArrayRef<std::unique_ptr<SourceCoverageView>> Items,
203                    unsigned LineNo) {
204   auto PrevIdx = CurrentIdx;
205   auto E = Items.size();
206   while (CurrentIdx < E &&
207          Items[CurrentIdx]->getSubViewsExpansionLine() == LineNo)
208     ++CurrentIdx;
209   return ArrayRef<std::unique_ptr<SourceCoverageView>>(Items.data() + PrevIdx,
210                                                        CurrentIdx - PrevIdx);
211 }
212
213 void SourceCoverageView::render(raw_ostream &OS, unsigned Offset) {
214   // Make sure that the children are in sorted order.
215   sortChildren();
216
217   SmallVector<HighlightRange, 8> AdjustedLineHighlightRanges;
218   size_t CurrentChild = 0;
219   size_t CurrentHighlightRange = 0;
220   size_t CurrentRegionMarker = 0;
221
222   line_iterator Lines(File);
223   // Advance the line iterator to the first line.
224   while (Lines.line_number() < LineStart)
225     ++Lines;
226
227   // The width of the leading columns
228   unsigned CombinedColumnWidth =
229       (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
230       (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
231   // The width of the line that is used to divide between the view and the
232   // subviews.
233   unsigned DividerWidth = CombinedColumnWidth + 4;
234
235   for (size_t I = 0; I < LineCount; ++I) {
236     unsigned LineNo = I + LineStart;
237
238     // Gather the child subviews that are visible on this line.
239     auto LineSubViews = gatherLineSubViews(CurrentChild, Children, LineNo);
240
241     renderOffset(OS, Offset);
242     if (Options.ShowLineStats)
243       renderLineCoverageColumn(OS, LineStats[I]);
244     if (Options.ShowLineNumbers)
245       renderLineNumberColumn(OS, LineNo);
246
247     // Gather highlighting ranges.
248     auto LineHighlightRanges =
249         gatherLineItems(CurrentHighlightRange, HighlightRanges, LineNo);
250     auto LineRanges = LineHighlightRanges;
251     // Highlight the expansion range if there is an expansion subview on this
252     // line.
253     if (!LineSubViews.empty() && LineSubViews.front()->isExpansionSubView() &&
254         Options.Colors) {
255       insertHighlightRange(LineHighlightRanges,
256                            LineSubViews.front()->getExpansionHighlightRange(),
257                            AdjustedLineHighlightRanges);
258       LineRanges = AdjustedLineHighlightRanges;
259     }
260
261     // Display the source code for the current line.
262     StringRef Line = *Lines;
263     // Check if the line is empty, as line_iterator skips blank lines.
264     if (LineNo < Lines.line_number())
265       Line = "";
266     else if (!Lines.is_at_eof())
267       ++Lines;
268     renderLine(OS, Line, LineRanges);
269
270     // Show the region markers.
271     bool ShowMarkers = !Options.ShowLineStatsOrRegionMarkers ||
272                        LineStats[I].hasMultipleRegions();
273     auto LineMarkers = gatherLineItems(CurrentRegionMarker, Markers, LineNo);
274     if (ShowMarkers && !LineMarkers.empty()) {
275       renderOffset(OS, Offset);
276       OS.indent(CombinedColumnWidth);
277       renderRegionMarkers(OS, LineMarkers);
278     }
279
280     // Show the line's expanded child subviews.
281     bool FirstChildExpansion = true;
282     if (LineSubViews.empty())
283       continue;
284     unsigned NewOffset = Offset + 1;
285     renderViewDivider(NewOffset, DividerWidth, OS);
286     OS << "\n";
287     for (const auto &Child : LineSubViews) {
288       // If this subview shows a function instantiation, render the function's
289       // name.
290       if (Child->isInstantiationSubView()) {
291         renderOffset(OS, NewOffset);
292         OS << ' ';
293         Options.colored_ostream(OS, raw_ostream::CYAN) << Child->FunctionName
294                                                        << ":";
295         OS << "\n";
296       } else {
297         if (!FirstChildExpansion) {
298           // Re-render the current line and highlight the expansion range for
299           // this
300           // subview.
301           insertHighlightRange(LineHighlightRanges,
302                                Child->getExpansionHighlightRange(),
303                                AdjustedLineHighlightRanges);
304           renderOffset(OS, Offset);
305           OS.indent(CombinedColumnWidth + (Offset == 0 ? 0 : 1));
306           renderLine(OS, Line, AdjustedLineHighlightRanges);
307           renderViewDivider(NewOffset, DividerWidth, OS);
308           OS << "\n";
309         } else
310           FirstChildExpansion = false;
311       }
312       // Render the child subview
313       Child->render(OS, NewOffset);
314       renderViewDivider(NewOffset, DividerWidth, OS);
315       OS << "\n";
316     }
317   }
318 }
319
320 void
321 SourceCoverageView::createLineCoverageInfo(SourceCoverageDataManager &Data) {
322   LineStats.resize(LineCount);
323   for (const auto &Region : Data.getSourceRegions()) {
324     auto Value = Region.second;
325     LineStats[Region.first.LineStart - LineStart].addRegionStartCount(Value);
326     for (unsigned Line = Region.first.LineStart + 1;
327          Line <= Region.first.LineEnd; ++Line)
328       LineStats[Line - LineStart].addRegionCount(Value);
329   }
330
331   // Reset the line stats for skipped regions.
332   for (const auto &Region : Data.getSkippedRegions()) {
333     for (unsigned Line = Region.LineStart; Line <= Region.LineEnd; ++Line)
334       LineStats[Line - LineStart] = LineCoverageInfo();
335   }
336 }
337
338 void
339 SourceCoverageView::createHighlightRanges(SourceCoverageDataManager &Data) {
340   auto Regions = Data.getSourceRegions();
341   std::vector<bool> AlreadyHighlighted;
342   AlreadyHighlighted.resize(Regions.size(), false);
343
344   for (size_t I = 0, S = Regions.size(); I < S; ++I) {
345     const auto &Region = Regions[I];
346     auto Value = Region.second;
347     auto SrcRange = Region.first;
348     if (Value != 0)
349       continue;
350     if (AlreadyHighlighted[I])
351       continue;
352     for (size_t J = 0; J < S; ++J) {
353       if (SrcRange.contains(Regions[J].first)) {
354         AlreadyHighlighted[J] = true;
355       }
356     }
357     if (SrcRange.LineStart == SrcRange.LineEnd) {
358       HighlightRanges.push_back(HighlightRange(
359           SrcRange.LineStart, SrcRange.ColumnStart, SrcRange.ColumnEnd));
360       continue;
361     }
362     HighlightRanges.push_back(
363         HighlightRange(SrcRange.LineStart, SrcRange.ColumnStart,
364                        std::numeric_limits<unsigned>::max()));
365     HighlightRanges.push_back(
366         HighlightRange(SrcRange.LineEnd, 1, SrcRange.ColumnEnd));
367     for (unsigned Line = SrcRange.LineStart + 1; Line < SrcRange.LineEnd;
368          ++Line) {
369       HighlightRanges.push_back(
370           HighlightRange(Line, 1, std::numeric_limits<unsigned>::max()));
371     }
372   }
373
374   std::sort(HighlightRanges.begin(), HighlightRanges.end());
375
376   if (Options.Debug) {
377     for (const auto &Range : HighlightRanges) {
378       outs() << "Highlighted line " << Range.Line << ", " << Range.ColumnStart
379              << " -> ";
380       if (Range.ColumnEnd == std::numeric_limits<unsigned>::max()) {
381         outs() << "?\n";
382       } else {
383         outs() << Range.ColumnEnd << "\n";
384       }
385     }
386   }
387 }
388
389 void SourceCoverageView::createRegionMarkers(SourceCoverageDataManager &Data) {
390   for (const auto &Region : Data.getSourceRegions()) {
391     if (Region.first.LineStart >= LineStart)
392       Markers.push_back(RegionMarker(Region.first.LineStart,
393                                      Region.first.ColumnStart, Region.second));
394   }
395
396   if (Options.Debug) {
397     for (const auto &Marker : Markers) {
398       outs() << "Marker at " << Marker.Line << ":" << Marker.Column << " = "
399              << Marker.ExecutionCount << "\n";
400     }
401   }
402 }
403
404 void SourceCoverageView::load(SourceCoverageDataManager &Data) {
405   if (Options.ShowLineStats)
406     createLineCoverageInfo(Data);
407   if (Options.Colors)
408     createHighlightRanges(Data);
409   if (Options.ShowRegionMarkers)
410     createRegionMarkers(Data);
411 }