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 /// \file This class implements rendering for code coverage of source code.
12 //===----------------------------------------------------------------------===//
14 #include "SourceCoverageView.h"
15 #include "SourceCoverageViewHTML.h"
16 #include "SourceCoverageViewText.h"
17 #include "llvm/ADT/SmallString.h"
18 #include "llvm/ADT/StringExtras.h"
19 #include "llvm/Support/FileSystem.h"
20 #include "llvm/Support/LineIterator.h"
21 #include "llvm/Support/Path.h"
25 void CoveragePrinter::StreamDestructor::operator()(raw_ostream
*OS
) const {
31 std::string
CoveragePrinter::getOutputPath(StringRef Path
, StringRef Extension
,
33 bool Relative
) const {
34 assert(!Extension
.empty() && "The file extension may not be empty");
36 SmallString
<256> FullPath
;
39 FullPath
.append(Opts
.ShowOutputDirectory
);
42 sys::path::append(FullPath
, getCoverageDir());
44 SmallString
<256> ParentPath
= sys::path::parent_path(Path
);
45 sys::path::remove_dots(ParentPath
, /*remove_dot_dots=*/true);
46 sys::path::append(FullPath
, sys::path::relative_path(ParentPath
));
48 auto PathFilename
= (sys::path::filename(Path
) + "." + Extension
).str();
49 sys::path::append(FullPath
, PathFilename
);
50 sys::path::native(FullPath
);
52 return FullPath
.str();
55 Expected
<CoveragePrinter::OwnedStream
>
56 CoveragePrinter::createOutputStream(StringRef Path
, StringRef Extension
,
57 bool InToplevel
) const {
58 if (!Opts
.hasOutputDirectory())
59 return OwnedStream(&outs());
61 std::string FullPath
= getOutputPath(Path
, Extension
, InToplevel
, false);
63 auto ParentDir
= sys::path::parent_path(FullPath
);
64 if (auto E
= sys::fs::create_directories(ParentDir
))
65 return errorCodeToError(E
);
68 raw_ostream
*RawStream
=
69 new raw_fd_ostream(FullPath
, E
, sys::fs::FA_Read
| sys::fs::FA_Write
);
70 auto OS
= CoveragePrinter::OwnedStream(RawStream
);
72 return errorCodeToError(E
);
76 std::unique_ptr
<CoveragePrinter
>
77 CoveragePrinter::create(const CoverageViewOptions
&Opts
) {
78 switch (Opts
.Format
) {
79 case CoverageViewOptions::OutputFormat::Text
:
80 return llvm::make_unique
<CoveragePrinterText
>(Opts
);
81 case CoverageViewOptions::OutputFormat::HTML
:
82 return llvm::make_unique
<CoveragePrinterHTML
>(Opts
);
83 case CoverageViewOptions::OutputFormat::Lcov
:
84 // Unreachable because CodeCoverage.cpp should terminate with an error
85 // before we get here.
86 llvm_unreachable("Lcov format is not supported!");
88 llvm_unreachable("Unknown coverage output format!");
91 unsigned SourceCoverageView::getFirstUncoveredLineNo() {
92 const auto MinSegIt
= find_if(CoverageInfo
, [](const CoverageSegment
&S
) {
93 return S
.HasCount
&& S
.Count
== 0;
96 // There is no uncovered line, return zero.
97 if (MinSegIt
== CoverageInfo
.end())
100 return (*MinSegIt
).Line
;
103 std::string
SourceCoverageView::formatCount(uint64_t N
) {
104 std::string Number
= utostr(N
);
105 int Len
= Number
.size();
108 int IntLen
= Len
% 3 == 0 ? 3 : Len
% 3;
109 std::string
Result(Number
.data(), IntLen
);
111 Result
.push_back('.');
112 Result
+= Number
.substr(IntLen
, 3 - IntLen
);
114 Result
.push_back(" kMGTPEZY"[(Len
- 1) / 3]);
118 bool SourceCoverageView::shouldRenderRegionMarkers(
119 const LineCoverageStats
&LCS
) const {
120 if (!getOptions().ShowRegionMarkers
)
123 CoverageSegmentArray Segments
= LCS
.getLineSegments();
124 if (Segments
.empty())
126 for (unsigned I
= 0, E
= Segments
.size() - 1; I
< E
; ++I
) {
127 const auto *CurSeg
= Segments
[I
];
128 if (!CurSeg
->IsRegionEntry
|| CurSeg
->Count
== LCS
.getExecutionCount())
135 bool SourceCoverageView::hasSubViews() const {
136 return !ExpansionSubViews
.empty() || !InstantiationSubViews
.empty();
139 std::unique_ptr
<SourceCoverageView
>
140 SourceCoverageView::create(StringRef SourceName
, const MemoryBuffer
&File
,
141 const CoverageViewOptions
&Options
,
142 CoverageData
&&CoverageInfo
) {
143 switch (Options
.Format
) {
144 case CoverageViewOptions::OutputFormat::Text
:
145 return llvm::make_unique
<SourceCoverageViewText
>(
146 SourceName
, File
, Options
, std::move(CoverageInfo
));
147 case CoverageViewOptions::OutputFormat::HTML
:
148 return llvm::make_unique
<SourceCoverageViewHTML
>(
149 SourceName
, File
, Options
, std::move(CoverageInfo
));
150 case CoverageViewOptions::OutputFormat::Lcov
:
151 // Unreachable because CodeCoverage.cpp should terminate with an error
152 // before we get here.
153 llvm_unreachable("Lcov format is not supported!");
155 llvm_unreachable("Unknown coverage output format!");
158 std::string
SourceCoverageView::getSourceName() const {
159 SmallString
<128> SourceText(SourceName
);
160 sys::path::remove_dots(SourceText
, /*remove_dot_dots=*/true);
161 sys::path::native(SourceText
);
162 return SourceText
.str();
165 void SourceCoverageView::addExpansion(
166 const CounterMappingRegion
&Region
,
167 std::unique_ptr
<SourceCoverageView
> View
) {
168 ExpansionSubViews
.emplace_back(Region
, std::move(View
));
171 void SourceCoverageView::addInstantiation(
172 StringRef FunctionName
, unsigned Line
,
173 std::unique_ptr
<SourceCoverageView
> View
) {
174 InstantiationSubViews
.emplace_back(FunctionName
, Line
, std::move(View
));
177 void SourceCoverageView::print(raw_ostream
&OS
, bool WholeFile
,
178 bool ShowSourceName
, bool ShowTitle
,
179 unsigned ViewDepth
) {
181 renderTitle(OS
, "Coverage Report");
183 renderViewHeader(OS
);
186 renderSourceName(OS
, WholeFile
);
188 renderTableHeader(OS
, (ViewDepth
> 0) ? 0 : getFirstUncoveredLineNo(),
191 // We need the expansions and instantiations sorted so we can go through them
192 // while we iterate lines.
193 std::stable_sort(ExpansionSubViews
.begin(), ExpansionSubViews
.end());
194 std::stable_sort(InstantiationSubViews
.begin(), InstantiationSubViews
.end());
195 auto NextESV
= ExpansionSubViews
.begin();
196 auto EndESV
= ExpansionSubViews
.end();
197 auto NextISV
= InstantiationSubViews
.begin();
198 auto EndISV
= InstantiationSubViews
.end();
200 // Get the coverage information for the file.
201 auto StartSegment
= CoverageInfo
.begin();
202 auto EndSegment
= CoverageInfo
.end();
203 LineCoverageIterator LCI
{CoverageInfo
, 1};
204 LineCoverageIterator LCIEnd
= LCI
.getEnd();
206 unsigned FirstLine
= StartSegment
!= EndSegment
? StartSegment
->Line
: 0;
207 for (line_iterator
LI(File
, /*SkipBlanks=*/false); !LI
.is_at_eof();
209 // If we aren't rendering the whole file, we need to filter out the prologue
214 else if (LI
.line_number() < FirstLine
)
218 renderLinePrefix(OS
, ViewDepth
);
219 if (getOptions().ShowLineNumbers
)
220 renderLineNumberColumn(OS
, LI
.line_number());
222 if (getOptions().ShowLineStats
)
223 renderLineCoverageColumn(OS
, *LCI
);
225 // If there are expansion subviews, we want to highlight the first one.
226 unsigned ExpansionColumn
= 0;
227 if (NextESV
!= EndESV
&& NextESV
->getLine() == LI
.line_number() &&
229 ExpansionColumn
= NextESV
->getStartCol();
231 // Display the source code for the current line.
232 renderLine(OS
, {*LI
, LI
.line_number()}, *LCI
, ExpansionColumn
, ViewDepth
);
234 // Show the region markers.
235 if (shouldRenderRegionMarkers(*LCI
))
236 renderRegionMarkers(OS
, *LCI
, ViewDepth
);
238 // Show the expansions and instantiations for this line.
239 bool RenderedSubView
= false;
240 for (; NextESV
!= EndESV
&& NextESV
->getLine() == LI
.line_number();
242 renderViewDivider(OS
, ViewDepth
+ 1);
244 // Re-render the current line and highlight the expansion range for
246 if (RenderedSubView
) {
247 ExpansionColumn
= NextESV
->getStartCol();
248 renderExpansionSite(OS
, {*LI
, LI
.line_number()}, *LCI
, ExpansionColumn
,
250 renderViewDivider(OS
, ViewDepth
+ 1);
253 renderExpansionView(OS
, *NextESV
, ViewDepth
+ 1);
254 RenderedSubView
= true;
256 for (; NextISV
!= EndISV
&& NextISV
->Line
== LI
.line_number(); ++NextISV
) {
257 renderViewDivider(OS
, ViewDepth
+ 1);
258 renderInstantiationView(OS
, *NextISV
, ViewDepth
+ 1);
259 RenderedSubView
= true;
262 renderViewDivider(OS
, ViewDepth
+ 1);
263 renderLineSuffix(OS
, ViewDepth
);
266 renderViewFooter(OS
);