blob: 8aa99295a0f17ce38398bae71b074aa556cf20ff [file] [log] [blame]
Alex Lorenze82d89c2014-08-22 22:56:03 +00001//===- 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"
Justin Bognerfe357c02014-09-17 18:23:47 +000015#include "llvm/ADT/Optional.h"
Alex Lorenze82d89c2014-08-22 22:56:03 +000016#include "llvm/ADT/SmallString.h"
Justin Bognerd0ceebf2015-05-13 22:41:48 +000017#include "llvm/ADT/StringExtras.h"
Alex Lorenze82d89c2014-08-22 22:56:03 +000018#include "llvm/Support/LineIterator.h"
19
20using namespace llvm;
21
Justin Bogner953e2402014-09-20 15:31:56 +000022void SourceCoverageView::renderLine(
23 raw_ostream &OS, StringRef Line, int64_t LineNumber,
24 const coverage::CoverageSegment *WrappedSegment,
25 ArrayRef<const coverage::CoverageSegment *> Segments,
26 unsigned ExpansionCol) {
Justin Bognerfe357c02014-09-17 18:23:47 +000027 Optional<raw_ostream::Colors> Highlight;
28 SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
Alex Lorenze82d89c2014-08-22 22:56:03 +000029
Justin Bognerfe357c02014-09-17 18:23:47 +000030 // The first segment overlaps from a previous line, so we treat it specially.
31 if (WrappedSegment && WrappedSegment->HasCount && WrappedSegment->Count == 0)
32 Highlight = raw_ostream::RED;
Alex Lorenze82d89c2014-08-22 22:56:03 +000033
Justin Bognerfe357c02014-09-17 18:23:47 +000034 // Output each segment of the line, possibly highlighted.
35 unsigned Col = 1;
36 for (const auto *S : Segments) {
37 unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);
38 colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
Vedant Kumar1c4f5882016-06-24 00:41:26 +000039 getOptions().Colors && Highlight, /*Bold=*/false,
40 /*BG=*/true)
Justin Bognerfe357c02014-09-17 18:23:47 +000041 << Line.substr(Col - 1, End - Col);
Vedant Kumar1c4f5882016-06-24 00:41:26 +000042 if (getOptions().Debug && Highlight)
Justin Bognerfe357c02014-09-17 18:23:47 +000043 HighlightedRanges.push_back(std::make_pair(Col, End));
44 Col = End;
45 if (Col == ExpansionCol)
46 Highlight = raw_ostream::CYAN;
47 else if (S->HasCount && S->Count == 0)
48 Highlight = raw_ostream::RED;
49 else
50 Highlight = None;
Alex Lorenze82d89c2014-08-22 22:56:03 +000051 }
52
53 // Show the rest of the line
Justin Bognerfe357c02014-09-17 18:23:47 +000054 colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
Vedant Kumar1c4f5882016-06-24 00:41:26 +000055 getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true)
Justin Bognerfe357c02014-09-17 18:23:47 +000056 << Line.substr(Col - 1, Line.size() - Col + 1);
Alex Lorenze82d89c2014-08-22 22:56:03 +000057 OS << "\n";
Justin Bogner92bb3022014-09-15 22:23:29 +000058
Vedant Kumar1c4f5882016-06-24 00:41:26 +000059 if (getOptions().Debug) {
Justin Bognerfe357c02014-09-17 18:23:47 +000060 for (const auto &Range : HighlightedRanges)
61 errs() << "Highlighted line " << LineNumber << ", " << Range.first
62 << " -> " << Range.second << "\n";
63 if (Highlight)
64 errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n";
Justin Bogner92bb3022014-09-15 22:23:29 +000065 }
Alex Lorenze82d89c2014-08-22 22:56:03 +000066}
67
Justin Bogner76e251c2014-09-16 06:21:57 +000068void SourceCoverageView::renderIndent(raw_ostream &OS, unsigned Level) {
69 for (unsigned I = 0; I < Level; ++I)
Alex Lorenze82d89c2014-08-22 22:56:03 +000070 OS << " |";
71}
72
Justin Bogner76e251c2014-09-16 06:21:57 +000073void SourceCoverageView::renderViewDivider(unsigned Level, unsigned Length,
Alex Lorenze82d89c2014-08-22 22:56:03 +000074 raw_ostream &OS) {
Justin Bogner76e251c2014-09-16 06:21:57 +000075 assert(Level != 0 && "Cannot render divider at top level");
76 renderIndent(OS, Level - 1);
77 OS.indent(2);
Alex Lorenze82d89c2014-08-22 22:56:03 +000078 for (unsigned I = 0; I < Length; ++I)
79 OS << "-";
80}
81
Justin Bognerd0ceebf2015-05-13 22:41:48 +000082/// Format a count using engineering notation with 3 significant digits.
83static std::string formatCount(uint64_t N) {
84 std::string Number = utostr(N);
85 int Len = Number.size();
86 if (Len <= 3)
87 return Number;
88 int IntLen = Len % 3 == 0 ? 3 : Len % 3;
89 std::string Result(Number.data(), IntLen);
90 if (IntLen != 3) {
91 Result.push_back('.');
92 Result += Number.substr(IntLen, 3 - IntLen);
93 }
94 Result.push_back(" kMGTPEZY"[(Len - 1) / 3]);
95 return Result;
96}
97
Alex Lorenze82d89c2014-08-22 22:56:03 +000098void
99SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,
Vedant Kumar60dcb482016-06-24 00:34:48 +0000100 const LineCoverageStats &Line) {
Alex Lorenze82d89c2014-08-22 22:56:03 +0000101 if (!Line.isMapped()) {
102 OS.indent(LineCoverageColumnWidth) << '|';
103 return;
104 }
Justin Bognerd0ceebf2015-05-13 22:41:48 +0000105 std::string C = formatCount(Line.ExecutionCount);
106 OS.indent(LineCoverageColumnWidth - C.size());
Alex Lorenze82d89c2014-08-22 22:56:03 +0000107 colored_ostream(OS, raw_ostream::MAGENTA,
Vedant Kumar1c4f5882016-06-24 00:41:26 +0000108 Line.hasMultipleRegions() && getOptions().Colors)
Justin Bognerd0ceebf2015-05-13 22:41:48 +0000109 << C;
Alex Lorenze82d89c2014-08-22 22:56:03 +0000110 OS << '|';
111}
112
113void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
114 unsigned LineNo) {
115 SmallString<32> Buffer;
116 raw_svector_ostream BufferOS(Buffer);
117 BufferOS << LineNo;
118 auto Str = BufferOS.str();
119 // Trim and align to the right
120 Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
121 OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
122}
123
Justin Bognerfe357c02014-09-17 18:23:47 +0000124void SourceCoverageView::renderRegionMarkers(
Justin Bogner953e2402014-09-20 15:31:56 +0000125 raw_ostream &OS, ArrayRef<const coverage::CoverageSegment *> Segments) {
Alex Lorenze82d89c2014-08-22 22:56:03 +0000126 unsigned PrevColumn = 1;
Justin Bognerfe357c02014-09-17 18:23:47 +0000127 for (const auto *S : Segments) {
128 if (!S->IsRegionEntry)
129 continue;
Alex Lorenze82d89c2014-08-22 22:56:03 +0000130 // Skip to the new region
Justin Bognerfe357c02014-09-17 18:23:47 +0000131 if (S->Col > PrevColumn)
132 OS.indent(S->Col - PrevColumn);
133 PrevColumn = S->Col + 1;
Justin Bognerd0ceebf2015-05-13 22:41:48 +0000134 std::string C = formatCount(S->Count);
135 PrevColumn += C.size();
136 OS << '^' << C;
Alex Lorenze82d89c2014-08-22 22:56:03 +0000137 }
138 OS << "\n";
Justin Bogner0b3614f2014-09-15 22:12:28 +0000139
Vedant Kumar1c4f5882016-06-24 00:41:26 +0000140 if (getOptions().Debug)
Justin Bognerfe357c02014-09-17 18:23:47 +0000141 for (const auto *S : Segments)
Justin Bognerd0ceebf2015-05-13 22:41:48 +0000142 errs() << "Marker at " << S->Line << ":" << S->Col << " = "
143 << formatCount(S->Count) << (S->IsRegionEntry ? "\n" : " (pop)\n");
Alex Lorenze82d89c2014-08-22 22:56:03 +0000144}
145
Justin Bognerfe357c02014-09-17 18:23:47 +0000146void SourceCoverageView::render(raw_ostream &OS, bool WholeFile,
147 unsigned IndentLevel) {
Alex Lorenze82d89c2014-08-22 22:56:03 +0000148 // The width of the leading columns
149 unsigned CombinedColumnWidth =
Vedant Kumar1c4f5882016-06-24 00:41:26 +0000150 (getOptions().ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
151 (getOptions().ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
Alex Lorenze82d89c2014-08-22 22:56:03 +0000152 // The width of the line that is used to divide between the view and the
153 // subviews.
154 unsigned DividerWidth = CombinedColumnWidth + 4;
155
Justin Bogner5e1400a2014-09-17 05:33:20 +0000156 // We need the expansions and instantiations sorted so we can go through them
157 // while we iterate lines.
158 std::sort(ExpansionSubViews.begin(), ExpansionSubViews.end());
159 std::sort(InstantiationSubViews.begin(), InstantiationSubViews.end());
160 auto NextESV = ExpansionSubViews.begin();
161 auto EndESV = ExpansionSubViews.end();
162 auto NextISV = InstantiationSubViews.begin();
163 auto EndISV = InstantiationSubViews.end();
164
Justin Bognerfe357c02014-09-17 18:23:47 +0000165 // Get the coverage information for the file.
Justin Bogner953e2402014-09-20 15:31:56 +0000166 auto NextSegment = CoverageInfo.begin();
167 auto EndSegment = CoverageInfo.end();
Alex Lorenze82d89c2014-08-22 22:56:03 +0000168
Justin Bogner13ba23b2014-09-19 08:13:16 +0000169 unsigned FirstLine = NextSegment != EndSegment ? NextSegment->Line : 0;
Justin Bogner953e2402014-09-20 15:31:56 +0000170 const coverage::CoverageSegment *WrappedSegment = nullptr;
171 SmallVector<const coverage::CoverageSegment *, 8> LineSegments;
Justin Bognerfe357c02014-09-17 18:23:47 +0000172 for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof(); ++LI) {
173 // If we aren't rendering the whole file, we need to filter out the prologue
174 // and epilogue.
175 if (!WholeFile) {
176 if (NextSegment == EndSegment)
177 break;
Justin Bogner13ba23b2014-09-19 08:13:16 +0000178 else if (LI.line_number() < FirstLine)
Justin Bognerfe357c02014-09-17 18:23:47 +0000179 continue;
Alex Lorenze82d89c2014-08-22 22:56:03 +0000180 }
181
Justin Bognerfe357c02014-09-17 18:23:47 +0000182 // Collect the coverage information relevant to this line.
183 if (LineSegments.size())
184 WrappedSegment = LineSegments.back();
185 LineSegments.clear();
186 while (NextSegment != EndSegment && NextSegment->Line == LI.line_number())
187 LineSegments.push_back(&*NextSegment++);
188
189 // Calculate a count to be for the line as a whole.
Vedant Kumar60dcb482016-06-24 00:34:48 +0000190 LineCoverageStats LineCount;
Justin Bognerfe357c02014-09-17 18:23:47 +0000191 if (WrappedSegment && WrappedSegment->HasCount)
192 LineCount.addRegionCount(WrappedSegment->Count);
193 for (const auto *S : LineSegments)
194 if (S->HasCount && S->IsRegionEntry)
195 LineCount.addRegionStartCount(S->Count);
196
197 // Render the line prefix.
198 renderIndent(OS, IndentLevel);
Vedant Kumar1c4f5882016-06-24 00:41:26 +0000199 if (getOptions().ShowLineStats)
Justin Bognerfe357c02014-09-17 18:23:47 +0000200 renderLineCoverageColumn(OS, LineCount);
Vedant Kumar1c4f5882016-06-24 00:41:26 +0000201 if (getOptions().ShowLineNumbers)
Justin Bognerfe357c02014-09-17 18:23:47 +0000202 renderLineNumberColumn(OS, LI.line_number());
203
204 // If there are expansion subviews, we want to highlight the first one.
205 unsigned ExpansionColumn = 0;
206 if (NextESV != EndESV && NextESV->getLine() == LI.line_number() &&
Vedant Kumar1c4f5882016-06-24 00:41:26 +0000207 getOptions().Colors)
Justin Bognerfe357c02014-09-17 18:23:47 +0000208 ExpansionColumn = NextESV->getStartCol();
209
Alex Lorenze82d89c2014-08-22 22:56:03 +0000210 // Display the source code for the current line.
Justin Bognerfe357c02014-09-17 18:23:47 +0000211 renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments,
212 ExpansionColumn);
Alex Lorenze82d89c2014-08-22 22:56:03 +0000213
214 // Show the region markers.
Vedant Kumar1c4f5882016-06-24 00:41:26 +0000215 if (getOptions().ShowRegionMarkers &&
216 (!getOptions().ShowLineStatsOrRegionMarkers ||
217 LineCount.hasMultipleRegions()) &&
Justin Bognerfe357c02014-09-17 18:23:47 +0000218 !LineSegments.empty()) {
Justin Bogner76e251c2014-09-16 06:21:57 +0000219 renderIndent(OS, IndentLevel);
Alex Lorenze82d89c2014-08-22 22:56:03 +0000220 OS.indent(CombinedColumnWidth);
Justin Bognerfe357c02014-09-17 18:23:47 +0000221 renderRegionMarkers(OS, LineSegments);
Alex Lorenze82d89c2014-08-22 22:56:03 +0000222 }
223
Justin Bogner5e1400a2014-09-17 05:33:20 +0000224 // Show the expansions and instantiations for this line.
Justin Bogner76e251c2014-09-16 06:21:57 +0000225 unsigned NestedIndent = IndentLevel + 1;
Justin Bogner5e1400a2014-09-17 05:33:20 +0000226 bool RenderedSubView = false;
Justin Bognerfe357c02014-09-17 18:23:47 +0000227 for (; NextESV != EndESV && NextESV->getLine() == LI.line_number();
228 ++NextESV) {
Justin Bogner5e1400a2014-09-17 05:33:20 +0000229 renderViewDivider(NestedIndent, DividerWidth, OS);
230 OS << "\n";
231 if (RenderedSubView) {
232 // Re-render the current line and highlight the expansion range for
233 // this subview.
Justin Bognerfe357c02014-09-17 18:23:47 +0000234 ExpansionColumn = NextESV->getStartCol();
Justin Bogner5e1400a2014-09-17 05:33:20 +0000235 renderIndent(OS, IndentLevel);
236 OS.indent(CombinedColumnWidth + (IndentLevel == 0 ? 0 : 1));
Justin Bognerfe357c02014-09-17 18:23:47 +0000237 renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments,
238 ExpansionColumn);
Justin Bogner5e1400a2014-09-17 05:33:20 +0000239 renderViewDivider(NestedIndent, DividerWidth, OS);
Alex Lorenze82d89c2014-08-22 22:56:03 +0000240 OS << "\n";
Alex Lorenze82d89c2014-08-22 22:56:03 +0000241 }
242 // Render the child subview
Vedant Kumar1c4f5882016-06-24 00:41:26 +0000243 if (getOptions().Debug)
Justin Bogner5cbed6e2014-09-17 21:48:52 +0000244 errs() << "Expansion at line " << NextESV->getLine() << ", "
245 << NextESV->getStartCol() << " -> " << NextESV->getEndCol()
246 << "\n";
Justin Bognerfe357c02014-09-17 18:23:47 +0000247 NextESV->View->render(OS, false, NestedIndent);
Justin Bogner5e1400a2014-09-17 05:33:20 +0000248 RenderedSubView = true;
249 }
Justin Bognerfe357c02014-09-17 18:23:47 +0000250 for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) {
Justin Bogner5e1400a2014-09-17 05:33:20 +0000251 renderViewDivider(NestedIndent, DividerWidth, OS);
252 OS << "\n";
253 renderIndent(OS, NestedIndent);
254 OS << ' ';
Vedant Kumar9d70d0b2016-06-24 00:34:51 +0000255 NextISV->View->renderSourceName(OS);
Justin Bognerfe357c02014-09-17 18:23:47 +0000256 NextISV->View->render(OS, false, NestedIndent);
Justin Bogner5e1400a2014-09-17 05:33:20 +0000257 RenderedSubView = true;
258 }
259 if (RenderedSubView) {
Justin Bogner76e251c2014-09-16 06:21:57 +0000260 renderViewDivider(NestedIndent, DividerWidth, OS);
Alex Lorenze82d89c2014-08-22 22:56:03 +0000261 OS << "\n";
262 }
263 }
264}
Vedant Kumar9d70d0b2016-06-24 00:34:51 +0000265
266void SourceCoverageView::renderSourceName(raw_ostream &OS) {
267 getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName()
268 << ":\n";
269}