Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/tools/llvm-cov/SourceCoverageViewText.cpp
35231 views
1
//===- SourceCoverageViewText.cpp - A text-based code coverage view -------===//
2
//
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
6
//
7
//===----------------------------------------------------------------------===//
8
///
9
/// \file This file implements the text-based coverage renderer.
10
///
11
//===----------------------------------------------------------------------===//
12
13
#include "SourceCoverageViewText.h"
14
#include "CoverageReport.h"
15
#include "llvm/ADT/SmallString.h"
16
#include "llvm/ADT/StringExtras.h"
17
#include "llvm/Support/FileSystem.h"
18
#include "llvm/Support/Format.h"
19
#include "llvm/Support/Path.h"
20
#include <optional>
21
22
using namespace llvm;
23
24
Expected<CoveragePrinter::OwnedStream>
25
CoveragePrinterText::createViewFile(StringRef Path, bool InToplevel) {
26
return createOutputStream(Path, "txt", InToplevel);
27
}
28
29
void CoveragePrinterText::closeViewFile(OwnedStream OS) {
30
OS->operator<<('\n');
31
}
32
33
Error CoveragePrinterText::createIndexFile(
34
ArrayRef<std::string> SourceFiles, const CoverageMapping &Coverage,
35
const CoverageFiltersMatchAll &Filters) {
36
auto OSOrErr = createOutputStream("index", "txt", /*InToplevel=*/true);
37
if (Error E = OSOrErr.takeError())
38
return E;
39
auto OS = std::move(OSOrErr.get());
40
raw_ostream &OSRef = *OS.get();
41
42
CoverageReport Report(Opts, Coverage);
43
Report.renderFileReports(OSRef, SourceFiles, Filters);
44
45
Opts.colored_ostream(OSRef, raw_ostream::CYAN) << "\n"
46
<< Opts.getLLVMVersionString();
47
48
return Error::success();
49
}
50
51
struct CoveragePrinterTextDirectory::Reporter : public DirectoryCoverageReport {
52
CoveragePrinterTextDirectory &Printer;
53
54
Reporter(CoveragePrinterTextDirectory &Printer,
55
const coverage::CoverageMapping &Coverage,
56
const CoverageFiltersMatchAll &Filters)
57
: DirectoryCoverageReport(Printer.Opts, Coverage, Filters),
58
Printer(Printer) {}
59
60
Error generateSubDirectoryReport(SubFileReports &&SubFiles,
61
SubDirReports &&SubDirs,
62
FileCoverageSummary &&SubTotals) override {
63
auto &LCPath = SubTotals.Name;
64
assert(Options.hasOutputDirectory() &&
65
"No output directory for index file");
66
67
SmallString<128> OSPath = LCPath;
68
sys::path::append(OSPath, "index");
69
auto OSOrErr = Printer.createOutputStream(OSPath, "txt",
70
/*InToplevel=*/false);
71
if (auto E = OSOrErr.takeError())
72
return E;
73
auto OS = std::move(OSOrErr.get());
74
raw_ostream &OSRef = *OS.get();
75
76
std::vector<FileCoverageSummary> Reports;
77
for (auto &&SubDir : SubDirs)
78
Reports.push_back(std::move(SubDir.second.first));
79
for (auto &&SubFile : SubFiles)
80
Reports.push_back(std::move(SubFile.second));
81
82
CoverageReport Report(Options, Coverage);
83
Report.renderFileReports(OSRef, Reports, SubTotals, Filters.empty());
84
85
Options.colored_ostream(OSRef, raw_ostream::CYAN)
86
<< "\n"
87
<< Options.getLLVMVersionString();
88
89
return Error::success();
90
}
91
};
92
93
Error CoveragePrinterTextDirectory::createIndexFile(
94
ArrayRef<std::string> SourceFiles, const CoverageMapping &Coverage,
95
const CoverageFiltersMatchAll &Filters) {
96
if (SourceFiles.size() <= 1)
97
return CoveragePrinterText::createIndexFile(SourceFiles, Coverage, Filters);
98
99
Reporter Report(*this, Coverage, Filters);
100
auto TotalsOrErr = Report.prepareDirectoryReports(SourceFiles);
101
if (auto E = TotalsOrErr.takeError())
102
return E;
103
auto &LCPath = TotalsOrErr->Name;
104
105
auto TopIndexFilePath =
106
getOutputPath("index", "txt", /*InToplevel=*/true, /*Relative=*/false);
107
auto LCPIndexFilePath =
108
getOutputPath((LCPath + "index").str(), "txt", /*InToplevel=*/false,
109
/*Relative=*/false);
110
return errorCodeToError(
111
sys::fs::copy_file(LCPIndexFilePath, TopIndexFilePath));
112
}
113
114
namespace {
115
116
static const unsigned LineCoverageColumnWidth = 7;
117
static const unsigned LineNumberColumnWidth = 5;
118
119
/// Get the width of the leading columns.
120
unsigned getCombinedColumnWidth(const CoverageViewOptions &Opts) {
121
return (Opts.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
122
(Opts.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
123
}
124
125
/// The width of the line that is used to divide between the view and
126
/// the subviews.
127
unsigned getDividerWidth(const CoverageViewOptions &Opts) {
128
return getCombinedColumnWidth(Opts) + 4;
129
}
130
131
} // anonymous namespace
132
133
void SourceCoverageViewText::renderViewHeader(raw_ostream &) {}
134
135
void SourceCoverageViewText::renderViewFooter(raw_ostream &) {}
136
137
void SourceCoverageViewText::renderSourceName(raw_ostream &OS, bool WholeFile) {
138
getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName()
139
<< ":\n";
140
}
141
142
void SourceCoverageViewText::renderLinePrefix(raw_ostream &OS,
143
unsigned ViewDepth) {
144
for (unsigned I = 0; I < ViewDepth; ++I)
145
OS << " |";
146
}
147
148
void SourceCoverageViewText::renderLineSuffix(raw_ostream &, unsigned) {}
149
150
void SourceCoverageViewText::renderViewDivider(raw_ostream &OS,
151
unsigned ViewDepth) {
152
assert(ViewDepth != 0 && "Cannot render divider at top level");
153
renderLinePrefix(OS, ViewDepth - 1);
154
OS.indent(2);
155
unsigned Length = getDividerWidth(getOptions());
156
for (unsigned I = 0; I < Length; ++I)
157
OS << '-';
158
OS << '\n';
159
}
160
161
void SourceCoverageViewText::renderLine(raw_ostream &OS, LineRef L,
162
const LineCoverageStats &LCS,
163
unsigned ExpansionCol,
164
unsigned ViewDepth) {
165
StringRef Line = L.Line;
166
unsigned LineNumber = L.LineNo;
167
auto *WrappedSegment = LCS.getWrappedSegment();
168
CoverageSegmentArray Segments = LCS.getLineSegments();
169
170
std::optional<raw_ostream::Colors> Highlight;
171
SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
172
173
// The first segment overlaps from a previous line, so we treat it specially.
174
if (WrappedSegment && !WrappedSegment->IsGapRegion &&
175
WrappedSegment->HasCount && WrappedSegment->Count == 0)
176
Highlight = raw_ostream::RED;
177
178
// Output each segment of the line, possibly highlighted.
179
unsigned Col = 1;
180
for (const auto *S : Segments) {
181
unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);
182
colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
183
getOptions().Colors && Highlight, /*Bold=*/false,
184
/*BG=*/true)
185
<< Line.substr(Col - 1, End - Col);
186
if (getOptions().Debug && Highlight)
187
HighlightedRanges.push_back(std::make_pair(Col, End));
188
Col = End;
189
if ((!S->IsGapRegion || (Highlight && *Highlight == raw_ostream::RED)) &&
190
S->HasCount && S->Count == 0)
191
Highlight = raw_ostream::RED;
192
else if (Col == ExpansionCol)
193
Highlight = raw_ostream::CYAN;
194
else
195
Highlight = std::nullopt;
196
}
197
198
// Show the rest of the line.
199
colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
200
getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true)
201
<< Line.substr(Col - 1, Line.size() - Col + 1);
202
OS << '\n';
203
204
if (getOptions().Debug) {
205
for (const auto &Range : HighlightedRanges)
206
errs() << "Highlighted line " << LineNumber << ", " << Range.first
207
<< " -> " << Range.second << '\n';
208
if (Highlight)
209
errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n";
210
}
211
}
212
213
void SourceCoverageViewText::renderLineCoverageColumn(
214
raw_ostream &OS, const LineCoverageStats &Line) {
215
if (!Line.isMapped()) {
216
OS.indent(LineCoverageColumnWidth) << '|';
217
return;
218
}
219
std::string C = formatCount(Line.getExecutionCount());
220
OS.indent(LineCoverageColumnWidth - C.size());
221
colored_ostream(OS, raw_ostream::MAGENTA,
222
Line.hasMultipleRegions() && getOptions().Colors)
223
<< C;
224
OS << '|';
225
}
226
227
void SourceCoverageViewText::renderLineNumberColumn(raw_ostream &OS,
228
unsigned LineNo) {
229
SmallString<32> Buffer;
230
raw_svector_ostream BufferOS(Buffer);
231
BufferOS << LineNo;
232
auto Str = BufferOS.str();
233
// Trim and align to the right.
234
Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
235
OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
236
}
237
238
void SourceCoverageViewText::renderRegionMarkers(raw_ostream &OS,
239
const LineCoverageStats &Line,
240
unsigned ViewDepth) {
241
renderLinePrefix(OS, ViewDepth);
242
OS.indent(getCombinedColumnWidth(getOptions()));
243
244
CoverageSegmentArray Segments = Line.getLineSegments();
245
246
// Just consider the segments which start *and* end on this line.
247
if (Segments.size() > 1)
248
Segments = Segments.drop_back();
249
250
unsigned PrevColumn = 1;
251
for (const auto *S : Segments) {
252
if (!S->IsRegionEntry)
253
continue;
254
if (S->Count == Line.getExecutionCount())
255
continue;
256
// Skip to the new region.
257
if (S->Col > PrevColumn)
258
OS.indent(S->Col - PrevColumn);
259
PrevColumn = S->Col + 1;
260
std::string C = formatCount(S->Count);
261
PrevColumn += C.size();
262
OS << '^' << C;
263
264
if (getOptions().Debug)
265
errs() << "Marker at " << S->Line << ":" << S->Col << " = "
266
<< formatCount(S->Count) << "\n";
267
}
268
OS << '\n';
269
}
270
271
void SourceCoverageViewText::renderExpansionSite(raw_ostream &OS, LineRef L,
272
const LineCoverageStats &LCS,
273
unsigned ExpansionCol,
274
unsigned ViewDepth) {
275
renderLinePrefix(OS, ViewDepth);
276
OS.indent(getCombinedColumnWidth(getOptions()) + (ViewDepth == 0 ? 0 : 1));
277
renderLine(OS, L, LCS, ExpansionCol, ViewDepth);
278
}
279
280
void SourceCoverageViewText::renderExpansionView(raw_ostream &OS,
281
ExpansionView &ESV,
282
unsigned ViewDepth) {
283
// Render the child subview.
284
if (getOptions().Debug)
285
errs() << "Expansion at line " << ESV.getLine() << ", " << ESV.getStartCol()
286
<< " -> " << ESV.getEndCol() << '\n';
287
ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
288
/*ShowTitle=*/false, ViewDepth + 1);
289
}
290
291
void SourceCoverageViewText::renderBranchView(raw_ostream &OS, BranchView &BRV,
292
unsigned ViewDepth) {
293
// Render the child subview.
294
if (getOptions().Debug)
295
errs() << "Branch at line " << BRV.getLine() << '\n';
296
297
for (const auto &R : BRV.Regions) {
298
double TruePercent = 0.0;
299
double FalsePercent = 0.0;
300
// FIXME: It may overflow when the data is too large, but I have not
301
// encountered it in actual use, and not sure whether to use __uint128_t.
302
uint64_t Total = R.ExecutionCount + R.FalseExecutionCount;
303
304
if (!getOptions().ShowBranchCounts && Total != 0) {
305
TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0;
306
FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0;
307
}
308
309
renderLinePrefix(OS, ViewDepth);
310
OS << " Branch (" << R.LineStart << ":" << R.ColumnStart << "): [";
311
312
if (R.Folded) {
313
OS << "Folded - Ignored]\n";
314
continue;
315
}
316
317
colored_ostream(OS, raw_ostream::RED,
318
getOptions().Colors && !R.ExecutionCount,
319
/*Bold=*/false, /*BG=*/true)
320
<< "True";
321
322
if (getOptions().ShowBranchCounts)
323
OS << ": " << formatCount(R.ExecutionCount) << ", ";
324
else
325
OS << ": " << format("%0.2f", TruePercent) << "%, ";
326
327
colored_ostream(OS, raw_ostream::RED,
328
getOptions().Colors && !R.FalseExecutionCount,
329
/*Bold=*/false, /*BG=*/true)
330
<< "False";
331
332
if (getOptions().ShowBranchCounts)
333
OS << ": " << formatCount(R.FalseExecutionCount);
334
else
335
OS << ": " << format("%0.2f", FalsePercent) << "%";
336
OS << "]\n";
337
}
338
}
339
340
void SourceCoverageViewText::renderMCDCView(raw_ostream &OS, MCDCView &MRV,
341
unsigned ViewDepth) {
342
for (auto &Record : MRV.Records) {
343
renderLinePrefix(OS, ViewDepth);
344
OS << "---> MC/DC Decision Region (";
345
// Display Line + Column information.
346
const CounterMappingRegion &DecisionRegion = Record.getDecisionRegion();
347
OS << DecisionRegion.LineStart << ":";
348
OS << DecisionRegion.ColumnStart << ") to (";
349
OS << DecisionRegion.LineEnd << ":";
350
OS << DecisionRegion.ColumnEnd << ")\n";
351
renderLinePrefix(OS, ViewDepth);
352
OS << "\n";
353
354
// Display MC/DC Information.
355
renderLinePrefix(OS, ViewDepth);
356
OS << " Number of Conditions: " << Record.getNumConditions() << "\n";
357
for (unsigned i = 0; i < Record.getNumConditions(); i++) {
358
renderLinePrefix(OS, ViewDepth);
359
OS << " " << Record.getConditionHeaderString(i);
360
}
361
renderLinePrefix(OS, ViewDepth);
362
OS << "\n";
363
renderLinePrefix(OS, ViewDepth);
364
OS << " Executed MC/DC Test Vectors:\n";
365
renderLinePrefix(OS, ViewDepth);
366
OS << "\n";
367
renderLinePrefix(OS, ViewDepth);
368
OS << " ";
369
OS << Record.getTestVectorHeaderString();
370
for (unsigned i = 0; i < Record.getNumTestVectors(); i++) {
371
renderLinePrefix(OS, ViewDepth);
372
OS << Record.getTestVectorString(i);
373
}
374
renderLinePrefix(OS, ViewDepth);
375
OS << "\n";
376
for (unsigned i = 0; i < Record.getNumConditions(); i++) {
377
renderLinePrefix(OS, ViewDepth);
378
OS << Record.getConditionCoverageString(i);
379
}
380
renderLinePrefix(OS, ViewDepth);
381
OS << " MC/DC Coverage for Decision: ";
382
colored_ostream(OS, raw_ostream::RED,
383
getOptions().Colors && Record.getPercentCovered() < 100.0,
384
/*Bold=*/false, /*BG=*/true)
385
<< format("%0.2f", Record.getPercentCovered()) << "%";
386
OS << "\n";
387
renderLinePrefix(OS, ViewDepth);
388
OS << "\n";
389
}
390
}
391
392
void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,
393
InstantiationView &ISV,
394
unsigned ViewDepth) {
395
renderLinePrefix(OS, ViewDepth);
396
OS << ' ';
397
if (!ISV.View)
398
getOptions().colored_ostream(OS, raw_ostream::RED)
399
<< "Unexecuted instantiation: " << ISV.FunctionName << "\n";
400
else
401
ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true,
402
/*ShowTitle=*/false, ViewDepth);
403
}
404
405
void SourceCoverageViewText::renderTitle(raw_ostream &OS, StringRef Title) {
406
if (getOptions().hasProjectTitle())
407
getOptions().colored_ostream(OS, raw_ostream::CYAN)
408
<< getOptions().ProjectTitle << "\n";
409
410
getOptions().colored_ostream(OS, raw_ostream::CYAN) << Title << "\n";
411
412
if (getOptions().hasCreatedTime())
413
getOptions().colored_ostream(OS, raw_ostream::CYAN)
414
<< getOptions().CreatedTimeStr << "\n";
415
}
416
417
void SourceCoverageViewText::renderTableHeader(raw_ostream &, unsigned) {}
418
419