Mercurial > hg > CbC > CbC_llvm
comparison tools/llvm-cov/SourceCoverageView.cpp @ 77:54457678186b LLVM3.6
LLVM 3.6
author | Kaito Tokumori <e105711@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 08 Sep 2014 22:06:00 +0900 |
parents | |
children | 60c9769439b8 |
comparison
equal
deleted
inserted
replaced
34:e874dbf0ad9d | 77:54457678186b |
---|---|
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 } |