llvm-cov: Make debug output more consistent
[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   if (Options.Debug) {
54     for (const auto &Range : Ranges) {
55       errs() << "Highlighted line " << Range.Line << ", " << Range.ColumnStart
56              << " -> ";
57       if (Range.ColumnEnd == std::numeric_limits<unsigned>::max()) {
58         errs() << "?\n";
59       } else {
60         errs() << Range.ColumnEnd << "\n";
61       }
62     }
63   }
64 }
65
66 void SourceCoverageView::renderOffset(raw_ostream &OS, unsigned I) {
67   for (unsigned J = 0; J < I; ++J)
68     OS << "  |";
69 }
70
71 void SourceCoverageView::renderViewDivider(unsigned Offset, unsigned Length,
72                                            raw_ostream &OS) {
73   for (unsigned J = 1; J < Offset; ++J)
74     OS << "  |";
75   if (Offset != 0)
76     OS.indent(2);
77   for (unsigned I = 0; I < Length; ++I)
78     OS << "-";
79 }
80
81 void
82 SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,
83                                              const LineCoverageInfo &Line) {
84   if (!Line.isMapped()) {
85     OS.indent(LineCoverageColumnWidth) << '|';
86     return;
87   }
88   SmallString<32> Buffer;
89   raw_svector_ostream BufferOS(Buffer);
90   BufferOS << Line.ExecutionCount;
91   auto Str = BufferOS.str();
92   // Trim
93   Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth));
94   // Align to the right
95   OS.indent(LineCoverageColumnWidth - Str.size());
96   colored_ostream(OS, raw_ostream::MAGENTA,
97                   Line.hasMultipleRegions() && Options.Colors)
98       << Str;
99   OS << '|';
100 }
101
102 void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
103                                                 unsigned LineNo) {
104   SmallString<32> Buffer;
105   raw_svector_ostream BufferOS(Buffer);
106   BufferOS << LineNo;
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 << '|';
111 }
112
113 void SourceCoverageView::renderRegionMarkers(raw_ostream &OS,
114                                              ArrayRef<RegionMarker> Regions) {
115   SmallString<32> Buffer;
116   raw_svector_ostream BufferOS(Buffer);
117
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();
129     OS << '^' << Str;
130     Buffer.clear();
131   }
132   OS << "\n";
133
134   if (Options.Debug) {
135     for (const auto &Region : Regions) {
136       errs() << "Marker at " << Region.Line << ":" << Region.Column << " = "
137              << Region.ExecutionCount << "\n";
138     }
139   }
140 }
141
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) {
148   Result.clear();
149   size_t I = 0;
150   auto E = Ranges.size();
151   for (; I < E; ++I) {
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,
161               Range.Kind));
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;
167       }
168       if (!NextRangeContainsInserted) {
169         ++I;
170         // Ignore ranges that are contained in inserted range
171         while (I < E && RangeToInsert.contains(Ranges[I]))
172           ++I;
173       }
174       break;
175     }
176     Result.push_back(Ranges[I]);
177   }
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));
186     ++I;
187   }
188   // Add the remaining ranges that are located after the inserted range
189   for (; I < E; ++I)
190     Result.push_back(Ranges[I]);
191 }
192
193 void SourceCoverageView::sortChildren() {
194   for (auto &I : Children)
195     I->sortChildren();
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;
200   });
201 }
202
203 SourceCoverageView::HighlightRange
204 SourceCoverageView::getExpansionHighlightRange() const {
205   return HighlightRange(ExpansionRegion.LineStart, ExpansionRegion.ColumnStart,
206                         ExpansionRegion.ColumnEnd, HighlightRange::Expanded);
207 }
208
209 template <typename T>
210 ArrayRef<T> gatherLineItems(size_t &CurrentIdx, const std::vector<T> &Items,
211                             unsigned LineNo) {
212   auto PrevIdx = CurrentIdx;
213   auto E = Items.size();
214   while (CurrentIdx < E && Items[CurrentIdx].Line == LineNo)
215     ++CurrentIdx;
216   return ArrayRef<T>(Items.data() + PrevIdx, CurrentIdx - PrevIdx);
217 }
218
219 ArrayRef<std::unique_ptr<SourceCoverageView>>
220 gatherLineSubViews(size_t &CurrentIdx,
221                    ArrayRef<std::unique_ptr<SourceCoverageView>> Items,
222                    unsigned LineNo) {
223   auto PrevIdx = CurrentIdx;
224   auto E = Items.size();
225   while (CurrentIdx < E &&
226          Items[CurrentIdx]->getSubViewsExpansionLine() == LineNo)
227     ++CurrentIdx;
228   return Items.slice(PrevIdx, CurrentIdx - PrevIdx);
229 }
230
231 void SourceCoverageView::render(raw_ostream &OS, unsigned Offset) {
232   // Make sure that the children are in sorted order.
233   sortChildren();
234
235   SmallVector<HighlightRange, 8> AdjustedLineHighlightRanges;
236   size_t CurrentChild = 0;
237   size_t CurrentHighlightRange = 0;
238   size_t CurrentRegionMarker = 0;
239
240   line_iterator Lines(File);
241   // Advance the line iterator to the first line.
242   while (Lines.line_number() < LineOffset)
243     ++Lines;
244
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
250   // subviews.
251   unsigned DividerWidth = CombinedColumnWidth + 4;
252
253   for (size_t I = 0, E = LineStats.size(); I < E; ++I) {
254     unsigned LineNo = I + LineOffset;
255
256     // Gather the child subviews that are visible on this line.
257     auto LineSubViews = gatherLineSubViews(CurrentChild, Children, LineNo);
258
259     renderOffset(OS, Offset);
260     if (Options.ShowLineStats)
261       renderLineCoverageColumn(OS, LineStats[I]);
262     if (Options.ShowLineNumbers)
263       renderLineNumberColumn(OS, LineNo);
264
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
270     // line.
271     if (!LineSubViews.empty() && LineSubViews.front()->isExpansionSubView() &&
272         Options.Colors) {
273       insertHighlightRange(LineHighlightRanges,
274                            LineSubViews.front()->getExpansionHighlightRange(),
275                            AdjustedLineHighlightRanges);
276       LineRanges = AdjustedLineHighlightRanges;
277     }
278
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())
283       Line = "";
284     else if (!Lines.is_at_eof())
285       ++Lines;
286     renderLine(OS, Line, LineRanges);
287
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);
296     }
297
298     // Show the line's expanded child subviews.
299     bool FirstChildExpansion = true;
300     if (LineSubViews.empty())
301       continue;
302     unsigned NewOffset = Offset + 1;
303     renderViewDivider(NewOffset, DividerWidth, OS);
304     OS << "\n";
305     for (const auto &Child : LineSubViews) {
306       // If this subview shows a function instantiation, render the function's
307       // name.
308       if (Child->isInstantiationSubView()) {
309         renderOffset(OS, NewOffset);
310         OS << ' ';
311         Options.colored_ostream(OS, raw_ostream::CYAN) << Child->FunctionName
312                                                        << ":";
313         OS << "\n";
314       } else {
315         if (!FirstChildExpansion) {
316           // Re-render the current line and highlight the expansion range for
317           // this
318           // subview.
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);
326           OS << "\n";
327         } else
328           FirstChildExpansion = false;
329       }
330       // Render the child subview
331       Child->render(OS, NewOffset);
332       renderViewDivider(NewOffset, DividerWidth, OS);
333       OS << "\n";
334     }
335   }
336 }
337
338 void SourceCoverageView::setUpVisibleRange(SourceCoverageDataManager &Data) {
339   auto CountedRegions = Data.getSourceRegions();
340   if (!CountedRegions.size())
341     return;
342
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);
347   }
348   LineOffset = Start;
349   LineStats.resize(End - Start + 1);
350 }
351
352 void
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;
359            ++Line)
360         LineStats[Line - LineOffset] = LineCoverageInfo();
361       continue;
362     }
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);
366   }
367 }
368
369 void
370 SourceCoverageView::createHighlightRanges(SourceCoverageDataManager &Data) {
371   auto CountedRegions = Data.getSourceRegions();
372   std::vector<bool> AlreadyHighlighted;
373   AlreadyHighlighted.resize(CountedRegions.size(), false);
374
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)
379       continue;
380     if (AlreadyHighlighted[I])
381       continue;
382     for (size_t J = 0; J < S; ++J) {
383       if (CR.contains(CountedRegions[J])) {
384         AlreadyHighlighted[J] = true;
385       }
386     }
387     if (CR.LineStart == CR.LineEnd) {
388       HighlightRanges.push_back(HighlightRange(
389           CR.LineStart, CR.ColumnStart, CR.ColumnEnd));
390       continue;
391     }
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;
398          ++Line) {
399       HighlightRanges.push_back(
400           HighlightRange(Line, 1, std::numeric_limits<unsigned>::max()));
401     }
402   }
403
404   std::sort(HighlightRanges.begin(), HighlightRanges.end());
405 }
406
407 void SourceCoverageView::createRegionMarkers(SourceCoverageDataManager &Data) {
408   for (const auto &CR : Data.getSourceRegions())
409     if (CR.Kind != coverage::CounterMappingRegion::SkippedRegion)
410       Markers.push_back(
411           RegionMarker(CR.LineStart, CR.ColumnStart, CR.ExecutionCount));
412 }
413
414 void SourceCoverageView::load(SourceCoverageDataManager &Data) {
415   setUpVisibleRange(Data);
416   if (Options.ShowLineStats)
417     createLineCoverageInfo(Data);
418   if (Options.Colors)
419     createHighlightRanges(Data);
420   if (Options.ShowRegionMarkers)
421     createRegionMarkers(Data);
422 }