1 //===- SourceCoverageView.cpp - Code coverage view for source code --------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 /// \file This class implements rendering for code coverage of source code.
11 //===----------------------------------------------------------------------===//
13 #include "SourceCoverageView.h"
14 #include "SourceCoverageViewHTML.h"
15 #include "SourceCoverageViewText.h"
16 #include "llvm/ADT/SmallString.h"
17 #include "llvm/ADT/StringExtras.h"
18 #include "llvm/Support/FileSystem.h"
19 #include "llvm/Support/LineIterator.h"
20 #include "llvm/Support/Path.h"
24 void CoveragePrinter::StreamDestructor::operator()(raw_ostream
*OS
) const {
30 std::string
CoveragePrinter::getOutputPath(StringRef Path
, StringRef Extension
,
32 bool Relative
) const {
33 assert(!Extension
.empty() && "The file extension may not be empty");
35 SmallString
<256> FullPath
;
38 FullPath
.append(Opts
.ShowOutputDirectory
);
41 sys::path::append(FullPath
, getCoverageDir());
43 SmallString
<256> ParentPath
= sys::path::parent_path(Path
);
44 sys::path::remove_dots(ParentPath
, /*remove_dot_dots=*/true);
45 sys::path::append(FullPath
, sys::path::relative_path(ParentPath
));
47 auto PathFilename
= (sys::path::filename(Path
) + "." + Extension
).str();
48 sys::path::append(FullPath
, PathFilename
);
49 sys::path::native(FullPath
);
51 return FullPath
.str();
54 Expected
<CoveragePrinter::OwnedStream
>
55 CoveragePrinter::createOutputStream(StringRef Path
, StringRef Extension
,
56 bool InToplevel
) const {
57 if (!Opts
.hasOutputDirectory())
58 return OwnedStream(&outs());
60 std::string FullPath
= getOutputPath(Path
, Extension
, InToplevel
, false);
62 auto ParentDir
= sys::path::parent_path(FullPath
);
63 if (auto E
= sys::fs::create_directories(ParentDir
))
64 return errorCodeToError(E
);
67 raw_ostream
*RawStream
=
68 new raw_fd_ostream(FullPath
, E
, sys::fs::FA_Read
| sys::fs::FA_Write
);
69 auto OS
= CoveragePrinter::OwnedStream(RawStream
);
71 return errorCodeToError(E
);
75 std::unique_ptr
<CoveragePrinter
>
76 CoveragePrinter::create(const CoverageViewOptions
&Opts
) {
77 switch (Opts
.Format
) {
78 case CoverageViewOptions::OutputFormat::Text
:
79 return llvm::make_unique
<CoveragePrinterText
>(Opts
);
80 case CoverageViewOptions::OutputFormat::HTML
:
81 return llvm::make_unique
<CoveragePrinterHTML
>(Opts
);
82 case CoverageViewOptions::OutputFormat::Lcov
:
83 // Unreachable because CodeCoverage.cpp should terminate with an error
84 // before we get here.
85 llvm_unreachable("Lcov format is not supported!");
87 llvm_unreachable("Unknown coverage output format!");
90 unsigned SourceCoverageView::getFirstUncoveredLineNo() {
91 const auto MinSegIt
= find_if(CoverageInfo
, [](const CoverageSegment
&S
) {
92 return S
.HasCount
&& S
.Count
== 0;
95 // There is no uncovered line, return zero.
96 if (MinSegIt
== CoverageInfo
.end())
99 return (*MinSegIt
).Line
;
102 std::string
SourceCoverageView::formatCount(uint64_t N
) {
103 std::string Number
= utostr(N
);
104 int Len
= Number
.size();
107 int IntLen
= Len
% 3 == 0 ? 3 : Len
% 3;
108 std::string
Result(Number
.data(), IntLen
);
110 Result
.push_back('.');
111 Result
+= Number
.substr(IntLen
, 3 - IntLen
);
113 Result
.push_back(" kMGTPEZY"[(Len
- 1) / 3]);
117 bool SourceCoverageView::shouldRenderRegionMarkers(
118 const LineCoverageStats
&LCS
) const {
119 if (!getOptions().ShowRegionMarkers
)
122 CoverageSegmentArray Segments
= LCS
.getLineSegments();
123 if (Segments
.empty())
125 for (unsigned I
= 0, E
= Segments
.size() - 1; I
< E
; ++I
) {
126 const auto *CurSeg
= Segments
[I
];
127 if (!CurSeg
->IsRegionEntry
|| CurSeg
->Count
== LCS
.getExecutionCount())
134 bool SourceCoverageView::hasSubViews() const {
135 return !ExpansionSubViews
.empty() || !InstantiationSubViews
.empty();
138 std::unique_ptr
<SourceCoverageView
>
139 SourceCoverageView::create(StringRef SourceName
, const MemoryBuffer
&File
,
140 const CoverageViewOptions
&Options
,
141 CoverageData
&&CoverageInfo
) {
142 switch (Options
.Format
) {
143 case CoverageViewOptions::OutputFormat::Text
:
144 return llvm::make_unique
<SourceCoverageViewText
>(
145 SourceName
, File
, Options
, std::move(CoverageInfo
));
146 case CoverageViewOptions::OutputFormat::HTML
:
147 return llvm::make_unique
<SourceCoverageViewHTML
>(
148 SourceName
, File
, Options
, std::move(CoverageInfo
));
149 case CoverageViewOptions::OutputFormat::Lcov
:
150 // Unreachable because CodeCoverage.cpp should terminate with an error
151 // before we get here.
152 llvm_unreachable("Lcov format is not supported!");
154 llvm_unreachable("Unknown coverage output format!");
157 std::string
SourceCoverageView::getSourceName() const {
158 SmallString
<128> SourceText(SourceName
);
159 sys::path::remove_dots(SourceText
, /*remove_dot_dots=*/true);
160 sys::path::native(SourceText
);
161 return SourceText
.str();
164 void SourceCoverageView::addExpansion(
165 const CounterMappingRegion
&Region
,
166 std::unique_ptr
<SourceCoverageView
> View
) {
167 ExpansionSubViews
.emplace_back(Region
, std::move(View
));
170 void SourceCoverageView::addInstantiation(
171 StringRef FunctionName
, unsigned Line
,
172 std::unique_ptr
<SourceCoverageView
> View
) {
173 InstantiationSubViews
.emplace_back(FunctionName
, Line
, std::move(View
));
176 void SourceCoverageView::print(raw_ostream
&OS
, bool WholeFile
,
177 bool ShowSourceName
, bool ShowTitle
,
178 unsigned ViewDepth
) {
180 renderTitle(OS
, "Coverage Report");
182 renderViewHeader(OS
);
185 renderSourceName(OS
, WholeFile
);
187 renderTableHeader(OS
, (ViewDepth
> 0) ? 0 : getFirstUncoveredLineNo(),
190 // We need the expansions and instantiations sorted so we can go through them
191 // while we iterate lines.
192 llvm::stable_sort(ExpansionSubViews
);
193 llvm::stable_sort(InstantiationSubViews
);
194 auto NextESV
= ExpansionSubViews
.begin();
195 auto EndESV
= ExpansionSubViews
.end();
196 auto NextISV
= InstantiationSubViews
.begin();
197 auto EndISV
= InstantiationSubViews
.end();
199 // Get the coverage information for the file.
200 auto StartSegment
= CoverageInfo
.begin();
201 auto EndSegment
= CoverageInfo
.end();
202 LineCoverageIterator LCI
{CoverageInfo
, 1};
203 LineCoverageIterator LCIEnd
= LCI
.getEnd();
205 unsigned FirstLine
= StartSegment
!= EndSegment
? StartSegment
->Line
: 0;
206 for (line_iterator
LI(File
, /*SkipBlanks=*/false); !LI
.is_at_eof();
208 // If we aren't rendering the whole file, we need to filter out the prologue
213 else if (LI
.line_number() < FirstLine
)
217 renderLinePrefix(OS
, ViewDepth
);
218 if (getOptions().ShowLineNumbers
)
219 renderLineNumberColumn(OS
, LI
.line_number());
221 if (getOptions().ShowLineStats
)
222 renderLineCoverageColumn(OS
, *LCI
);
224 // If there are expansion subviews, we want to highlight the first one.
225 unsigned ExpansionColumn
= 0;
226 if (NextESV
!= EndESV
&& NextESV
->getLine() == LI
.line_number() &&
228 ExpansionColumn
= NextESV
->getStartCol();
230 // Display the source code for the current line.
231 renderLine(OS
, {*LI
, LI
.line_number()}, *LCI
, ExpansionColumn
, ViewDepth
);
233 // Show the region markers.
234 if (shouldRenderRegionMarkers(*LCI
))
235 renderRegionMarkers(OS
, *LCI
, ViewDepth
);
237 // Show the expansions and instantiations for this line.
238 bool RenderedSubView
= false;
239 for (; NextESV
!= EndESV
&& NextESV
->getLine() == LI
.line_number();
241 renderViewDivider(OS
, ViewDepth
+ 1);
243 // Re-render the current line and highlight the expansion range for
245 if (RenderedSubView
) {
246 ExpansionColumn
= NextESV
->getStartCol();
247 renderExpansionSite(OS
, {*LI
, LI
.line_number()}, *LCI
, ExpansionColumn
,
249 renderViewDivider(OS
, ViewDepth
+ 1);
252 renderExpansionView(OS
, *NextESV
, ViewDepth
+ 1);
253 RenderedSubView
= true;
255 for (; NextISV
!= EndISV
&& NextISV
->Line
== LI
.line_number(); ++NextISV
) {
256 renderViewDivider(OS
, ViewDepth
+ 1);
257 renderInstantiationView(OS
, *NextISV
, ViewDepth
+ 1);
258 RenderedSubView
= true;
261 renderViewDivider(OS
, ViewDepth
+ 1);
262 renderLineSuffix(OS
, ViewDepth
);
265 renderViewFooter(OS
);