1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-18 10:32:48 +02:00

[Coverage] Add support for Branch Coverage in LLVM Source-Based Code Coverage

This is an enhancement to LLVM Source-Based Code Coverage in clang to track how
many times individual branch-generating conditions are taken (evaluate to TRUE)
and not taken (evaluate to FALSE).  Individual conditions may comprise larger
boolean expressions using boolean logical operators.  This functionality is
very similar to what is supported by GCOV except that it is very closely
anchored to the ASTs.

Differential Revision: https://reviews.llvm.org/D84467
This commit is contained in:
Alan Phipps 2020-12-28 11:20:48 -06:00
parent 99dc57aa1b
commit f85fa6973c
42 changed files with 1872 additions and 50 deletions

View File

@ -202,6 +202,11 @@ tool.
OPTIONS
^^^^^^^
.. option:: -show-branches=<VIEW>
Show coverage for branch conditions in terms of either count or percentage.
The supported views are: "count", "percent".
.. option:: -show-line-counts
Show the execution counts for each line. Defaults to true, unless another
@ -359,6 +364,10 @@ OPTIONS
universal binary or to use an architecture that does not match a
non-universal binary.
.. option:: -show-branch-summary
Show statistics for all branch conditions. Defaults to true.
.. option:: -show-functions
Show coverage summaries for each function. Defaults to false.
@ -390,9 +399,9 @@ The :program:`llvm-cov export` command exports coverage data of the binaries
*BIN*,... using the profile data *PROFILE* in either JSON or lcov trace file
format.
When exporting JSON, the regions, functions, expansions, and summaries of the
coverage data will be exported. When exporting an lcov trace file, the
line-based coverage and summaries will be exported.
When exporting JSON, the regions, functions, branches, expansions, and
summaries of the coverage data will be exported. When exporting an lcov trace
file, the line-based coverage, branch coverage, and summaries will be exported.
The exported data can optionally be filtered to only export the coverage
for the files listed in *SOURCES*.

View File

@ -126,6 +126,25 @@ There are several kinds of mapping regions:
<span style='background-color:#4A789C'> return </span><span style='background-color:#7FCA9F'>MAX</span><span style='background-color:#4A789C'>(x, 42); </span> <span class='c1'>// Expansion Region from 3:10 to 3:13</span>
<span style='background-color:#4A789C'>}</span>
</pre>`
* Branch regions associate instrumentable branch conditions in the source code
with a `coverage mapping counter`_ to track how many times an individual
condition evaluated to 'true' and another `coverage mapping counter`_ to
track how many times that condition evaluated to false. Instrumentable
branch conditions may comprise larger boolean expressions using boolean
logical operators. The 'true' and 'false' cases reflect unique branch paths
that can be traced back to the source code.
For example:
:raw-html:`<pre class='highlight' style='line-height:initial;'><span>int func(int x, int y) {
<span> if (<span style='background-color:#4A789C'>(x &gt; 1)</span> || <span style='background-color:#4A789C'>(y &gt; 3)</span>) {</span> <span class='c1'>// Branch Region from 3:6 to 3:12</span>
<span> </span><span class='c1'>// Branch Region from 3:17 to 3:23</span>
<span> printf("%d\n", x); </span>
<span> } else { </span>
<span> printf("\n"); </span>
<span> }</span>
<span> return 0; </span>
<span>}</span>
</pre>`
.. _source code range:
@ -198,6 +217,11 @@ counts for the unreachable lines and highlight the unreachable code.
Without them, the tool would think that those lines and regions were still
executed, as it doesn't possess the frontend's knowledge.
Note that branch regions are created to track branch conditions in the source
code and refer to two coverage mapping counters, one to track the number of
times the branch condition evaluated to "true", and one to track the number of
times the branch condition evaluated to "false".
LLVM IR Representation
======================
@ -242,7 +266,14 @@ too deeply).
[32 x i8] c"..." ; Encoded data (dissected later)
}, section "__llvm_covmap", align 8
The current version of the format is version 4. There are two differences from version 3:
The current version of the format is version 5. There is one difference from version 4:
* The notion of branch region has been introduced along with a corresponding
region kind. Branch regions encode two counters, one to track how many
times a "true" branch condition is taken, and one to track how many times a
"false" branch condition is taken.
There are two differences between versions 4 and 3:
* Function records are now named symbols, and are marked *linkonce_odr*. This
allows linkers to merge duplicate function records. Merging of duplicate
@ -511,7 +542,8 @@ or
``[pseudo-counter]``
The header encodes the region's counter and the region's kind.
The header encodes the region's counter and the region's kind. A branch region
will encode two counters.
The value of the counter's tag distinguishes between the counters and
pseudo-counters --- if the tag is zero, than this header contains a
@ -544,6 +576,7 @@ the ordinary counter. It has the following interpretation:
* 0 - This mapping region is a code region with a counter of zero.
* 2 - This mapping region is a skipped region.
* 4 - This mapping region is a branch region.
.. _source range:

View File

@ -90,6 +90,8 @@ private:
/// A Counter is an abstract value that describes how to compute the
/// execution count for a region of code using the collected profile count data.
struct Counter {
/// The CounterExpression kind (Add or Subtract) is encoded in bit 0 next to
/// the CounterKind. This means CounterKind has to leave bit 0 free.
enum CounterKind { Zero, CounterValueReference, Expression };
static const unsigned EncodingTagBits = 2;
static const unsigned EncodingTagMask = 0x3;
@ -219,10 +221,20 @@ struct CounterMappingRegion {
/// A GapRegion is like a CodeRegion, but its count is only set as the
/// line execution count when its the only region in the line.
GapRegion
GapRegion,
/// A BranchRegion represents leaf-level boolean expressions and is
/// associated with two counters, each representing the number of times the
/// expression evaluates to true or false.
BranchRegion
};
/// Primary Counter that is also used for Branch Regions (TrueCount).
Counter Count;
/// Secondary Counter used for Branch Regions (FalseCount).
Counter FalseCount;
unsigned FileID, ExpandedFileID;
unsigned LineStart, ColumnStart, LineEnd, ColumnEnd;
RegionKind Kind;
@ -234,6 +246,15 @@ struct CounterMappingRegion {
LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd),
ColumnEnd(ColumnEnd), Kind(Kind) {}
CounterMappingRegion(Counter Count, Counter FalseCount, unsigned FileID,
unsigned ExpandedFileID, unsigned LineStart,
unsigned ColumnStart, unsigned LineEnd,
unsigned ColumnEnd, RegionKind Kind)
: Count(Count), FalseCount(FalseCount), FileID(FileID),
ExpandedFileID(ExpandedFileID), LineStart(LineStart),
ColumnStart(ColumnStart), LineEnd(LineEnd), ColumnEnd(ColumnEnd),
Kind(Kind) {}
static CounterMappingRegion
makeRegion(Counter Count, unsigned FileID, unsigned LineStart,
unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) {
@ -263,6 +284,14 @@ struct CounterMappingRegion {
LineEnd, (1U << 31) | ColumnEnd, GapRegion);
}
static CounterMappingRegion
makeBranchRegion(Counter Count, Counter FalseCount, unsigned FileID,
unsigned LineStart, unsigned ColumnStart, unsigned LineEnd,
unsigned ColumnEnd) {
return CounterMappingRegion(Count, FalseCount, FileID, 0, LineStart,
ColumnStart, LineEnd, ColumnEnd, BranchRegion);
}
inline LineColPair startLoc() const {
return LineColPair(LineStart, ColumnStart);
}
@ -273,9 +302,17 @@ struct CounterMappingRegion {
/// Associates a source range with an execution count.
struct CountedRegion : public CounterMappingRegion {
uint64_t ExecutionCount;
uint64_t FalseExecutionCount;
bool Folded;
CountedRegion(const CounterMappingRegion &R, uint64_t ExecutionCount)
: CounterMappingRegion(R), ExecutionCount(ExecutionCount) {}
: CounterMappingRegion(R), ExecutionCount(ExecutionCount),
FalseExecutionCount(0), Folded(false) {}
CountedRegion(const CounterMappingRegion &R, uint64_t ExecutionCount,
uint64_t FalseExecutionCount)
: CounterMappingRegion(R), ExecutionCount(ExecutionCount),
FalseExecutionCount(FalseExecutionCount), Folded(false) {}
};
/// A Counter mapping context is used to connect the counters, expressions
@ -312,6 +349,8 @@ struct FunctionRecord {
std::vector<std::string> Filenames;
/// Regions in the function along with their counts.
std::vector<CountedRegion> CountedRegions;
/// Branch Regions in the function along with their counts.
std::vector<CountedRegion> CountedBranchRegions;
/// The number of times this function was executed.
uint64_t ExecutionCount = 0;
@ -321,10 +360,19 @@ struct FunctionRecord {
FunctionRecord(FunctionRecord &&FR) = default;
FunctionRecord &operator=(FunctionRecord &&) = default;
void pushRegion(CounterMappingRegion Region, uint64_t Count) {
void pushRegion(CounterMappingRegion Region, uint64_t Count,
uint64_t FalseCount) {
if (Region.Kind == CounterMappingRegion::BranchRegion) {
CountedBranchRegions.emplace_back(Region, Count, FalseCount);
// If both counters are hard-coded to zero, then this region represents a
// constant-folded branch.
if (Region.Count.isZero() && Region.FalseCount.isZero())
CountedBranchRegions.back().Folded = true;
return;
}
if (CountedRegions.empty())
ExecutionCount = Count;
CountedRegions.emplace_back(Region, Count);
CountedRegions.emplace_back(Region, Count, FalseCount);
}
};
@ -403,7 +451,8 @@ struct CoverageSegment {
IsRegionEntry(IsRegionEntry), IsGapRegion(false) {}
CoverageSegment(unsigned Line, unsigned Col, uint64_t Count,
bool IsRegionEntry, bool IsGapRegion = false)
bool IsRegionEntry, bool IsGapRegion = false,
bool IsBranchRegion = false)
: Line(Line), Col(Col), Count(Count), HasCount(true),
IsRegionEntry(IsRegionEntry), IsGapRegion(IsGapRegion) {}
@ -483,6 +532,7 @@ class CoverageData {
std::string Filename;
std::vector<CoverageSegment> Segments;
std::vector<ExpansionRecord> Expansions;
std::vector<CountedRegion> BranchRegions;
public:
CoverageData() = default;
@ -506,6 +556,9 @@ public:
/// Expansions that can be further processed.
ArrayRef<ExpansionRecord> getExpansions() const { return Expansions; }
/// Branches that can be further processed.
ArrayRef<CountedRegion> getBranches() const { return BranchRegions; }
};
/// The mapping of profile information to coverage data.
@ -941,7 +994,9 @@ enum CovMapVersion {
Version3 = 2,
// Function records are named, uniqued, and moved to a dedicated section.
Version4 = 3,
// The current version is Version4.
// Branch regions referring to two counters are added
Version5 = 4,
// The current version is Version5.
CurrentVersion = INSTR_PROF_COVMAP_VERSION
};

View File

@ -983,7 +983,9 @@ enum ProfVersion {
// In this version, the frontend PGO stable hash algorithm got fixed and
// may produce hashes different from Version5.
Version6 = 6,
// The current version is 5.
// An additional counter is added around logical operators.
Version7 = 7,
// The current version is 7.
CurrentVersion = INSTR_PROF_INDEX_VERSION
};
const uint64_t Version = ProfVersion::CurrentVersion;

View File

@ -647,9 +647,9 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
/* Raw profile format version (start from 1). */
#define INSTR_PROF_RAW_VERSION 5
/* Indexed profile format version (start from 1). */
#define INSTR_PROF_INDEX_VERSION 6
#define INSTR_PROF_INDEX_VERSION 7
/* Coverage mapping format version (start from 0). */
#define INSTR_PROF_COVMAP_VERSION 3
#define INSTR_PROF_COVMAP_VERSION 4
/* Profile version is always of type uint64_t. Reserve the upper 8 bits in the
* version for other variants of profile. We set the lowest bit of the upper 8

View File

@ -249,7 +249,12 @@ Error CoverageMapping::loadFunctionRecord(
consumeError(std::move(E));
return Error::success();
}
Function.pushRegion(Region, *ExecutionCount);
Expected<int64_t> AltExecutionCount = Ctx.evaluate(Region.FalseCount);
if (auto E = AltExecutionCount.takeError()) {
consumeError(std::move(E));
return Error::success();
}
Function.pushRegion(Region, *ExecutionCount, *AltExecutionCount);
}
// Don't create records for (filenames, function) pairs we've already seen.
@ -672,6 +677,10 @@ CoverageData CoverageMapping::getCoverageForFile(StringRef Filename) const {
if (MainFileID && isExpansion(CR, *MainFileID))
FileCoverage.Expansions.emplace_back(CR, Function);
}
// Capture branch regions specific to the function (excluding expansions).
for (const auto &CR : Function.CountedBranchRegions)
if (FileIDs.test(CR.FileID) && (CR.FileID == CR.ExpandedFileID))
FileCoverage.BranchRegions.push_back(CR);
}
LLVM_DEBUG(dbgs() << "Emitting segments for file: " << Filename << "\n");
@ -719,6 +728,10 @@ CoverageMapping::getCoverageForFunction(const FunctionRecord &Function) const {
if (isExpansion(CR, *MainFileID))
FunctionCoverage.Expansions.emplace_back(CR, Function);
}
// Capture branch regions specific to the function (excluding expansions).
for (const auto &CR : Function.CountedBranchRegions)
if (CR.FileID == *MainFileID)
FunctionCoverage.BranchRegions.push_back(CR);
LLVM_DEBUG(dbgs() << "Emitting segments for function: " << Function.Name
<< "\n");
@ -738,6 +751,10 @@ CoverageData CoverageMapping::getCoverageForExpansion(
if (isExpansion(CR, Expansion.FileID))
ExpansionCoverage.Expansions.emplace_back(CR, Expansion.Function);
}
for (const auto &CR : Expansion.Function.CountedBranchRegions)
// Capture branch regions that only pertain to the corresponding expansion.
if (CR.FileID == Expansion.FileID)
ExpansionCoverage.BranchRegions.push_back(CR);
LLVM_DEBUG(dbgs() << "Emitting segments for expansion of file "
<< Expansion.FileID << "\n");

View File

@ -213,7 +213,7 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
return Err;
unsigned LineStart = 0;
for (size_t I = 0; I < NumRegions; ++I) {
Counter C;
Counter C, C2;
CounterMappingRegion::RegionKind Kind = CounterMappingRegion::CodeRegion;
// Read the combined counter + region kind.
@ -223,6 +223,18 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
return Err;
unsigned Tag = EncodedCounterAndRegion & Counter::EncodingTagMask;
uint64_t ExpandedFileID = 0;
// If Tag does not represent a ZeroCounter, then it is understood to refer
// to a counter or counter expression with region kind assumed to be
// "CodeRegion". In that case, EncodedCounterAndRegion actually encodes the
// referenced counter or counter expression (and nothing else).
//
// If Tag represents a ZeroCounter and EncodingExpansionRegionBit is set,
// then EncodedCounterAndRegion is interpreted to represent an
// ExpansionRegion. In all other cases, EncodedCounterAndRegion is
// interpreted to refer to a specific region kind, after which additional
// fields may be read (e.g. BranchRegions have two encoded counters that
// follow an encoded region kind value).
if (Tag != Counter::Zero) {
if (auto Err = decodeCounter(EncodedCounterAndRegion, C))
return Err;
@ -243,6 +255,14 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
case CounterMappingRegion::SkippedRegion:
Kind = CounterMappingRegion::SkippedRegion;
break;
case CounterMappingRegion::BranchRegion:
// For a Branch Region, read two successive counters.
Kind = CounterMappingRegion::BranchRegion;
if (auto Err = readCounter(C))
return Err;
if (auto Err = readCounter(C2))
return Err;
break;
default:
return make_error<CoverageMapError>(coveragemap_error::malformed);
}
@ -294,7 +314,7 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
dbgs() << "\n";
});
auto CMR = CounterMappingRegion(C, InferredFileID, ExpandedFileID,
auto CMR = CounterMappingRegion(C, C2, InferredFileID, ExpandedFileID,
LineStart, ColumnStart,
LineStart + NumLines, ColumnEnd, Kind);
if (CMR.startLoc() > CMR.endLoc())
@ -600,7 +620,7 @@ public:
CovBuf += FilenamesSize;
FilenameRange FileRange(FilenamesBegin, Filenames.size() - FilenamesBegin);
if (Version == CovMapVersion::Version4) {
if (Version >= CovMapVersion::Version4) {
// Map a hash of the filenames region to the filename range associated
// with this coverage header.
int64_t FilenamesRef =
@ -628,7 +648,7 @@ public:
// This is a no-op in Version4 (coverage mappings are not affixed to the
// coverage header).
const char *MappingBuf = CovBuf;
if (Version == CovMapVersion::Version4 && CoverageSize != 0)
if (Version >= CovMapVersion::Version4 && CoverageSize != 0)
return make_error<CoverageMapError>(coveragemap_error::malformed);
CovBuf += CoverageSize;
const char *MappingEnd = CovBuf;
@ -682,7 +702,7 @@ public:
if (FileRange && !FileRange->isInvalid()) {
StringRef Mapping =
CFR->template getCoverageMapping<Endian>(OutOfLineMappingBuf);
if (Version == CovMapVersion::Version4 &&
if (Version >= CovMapVersion::Version4 &&
Mapping.data() + Mapping.size() > FuncRecBufEnd)
return make_error<CoverageMapError>(coveragemap_error::malformed);
if (Error Err = insertFunctionRecordIfNeeded(CFR, Mapping, *FileRange))
@ -711,6 +731,7 @@ Expected<std::unique_ptr<CovMapFuncRecordReader>> CovMapFuncRecordReader::get(
case CovMapVersion::Version2:
case CovMapVersion::Version3:
case CovMapVersion::Version4:
case CovMapVersion::Version5:
// Decompress the name data.
if (Error E = P.create(P.getNameData()))
return std::move(E);
@ -723,6 +744,9 @@ Expected<std::unique_ptr<CovMapFuncRecordReader>> CovMapFuncRecordReader::get(
else if (Version == CovMapVersion::Version4)
return std::make_unique<VersionedCovMapFuncRecordReader<
CovMapVersion::Version4, IntPtrT, Endian>>(P, R, F);
else if (Version == CovMapVersion::Version5)
return std::make_unique<VersionedCovMapFuncRecordReader<
CovMapVersion::Version5, IntPtrT, Endian>>(P, R, F);
}
llvm_unreachable("Unsupported version");
}
@ -766,7 +790,7 @@ static Error readCoverageMappingData(
}
// In Version4, function records are not affixed to coverage headers. Read
// the records from their dedicated section.
if (Version == CovMapVersion::Version4)
if (Version >= CovMapVersion::Version4)
return Reader->readFunctionRecords(FuncRecBuf, FuncRecBufEnd, None, nullptr,
nullptr);
return Error::success();

View File

@ -80,10 +80,14 @@ public:
ArrayRef<CounterMappingRegion> MappingRegions)
: Expressions(Expressions) {
AdjustedExpressionIDs.resize(Expressions.size(), 0);
for (const auto &I : MappingRegions)
for (const auto &I : MappingRegions) {
mark(I.Count);
for (const auto &I : MappingRegions)
mark(I.FalseCount);
}
for (const auto &I : MappingRegions) {
gatherUsed(I.Count);
gatherUsed(I.FalseCount);
}
}
void mark(Counter C) {
@ -201,6 +205,7 @@ void CoverageMappingWriter::write(raw_ostream &OS) {
PrevLineStart = 0;
}
Counter Count = Minimizer.adjust(I->Count);
Counter FalseCount = Minimizer.adjust(I->FalseCount);
switch (I->Kind) {
case CounterMappingRegion::CodeRegion:
case CounterMappingRegion::GapRegion:
@ -226,6 +231,13 @@ void CoverageMappingWriter::write(raw_ostream &OS) {
<< Counter::EncodingCounterTagAndExpansionRegionTagBits,
OS);
break;
case CounterMappingRegion::BranchRegion:
encodeULEB128(unsigned(I->Kind)
<< Counter::EncodingCounterTagAndExpansionRegionTagBits,
OS);
writeCounter(MinExpressions, Count, OS);
writeCounter(MinExpressions, FalseCount, OS);
break;
}
assert(I->LineStart >= PrevLineStart);
encodeULEB128(I->LineStart - PrevLineStart, OS);

View File

@ -1,24 +1,28 @@
CHECK: {"data":
CHECK-SAME: [{
CHECK-SAME: "files":[
CHECK-SAME: {"expansions":[],
CHECK-SAME: {"branches":[],
CHECK-SAME: "expansions":[],
CHECK-SAME: "filename":"/tmp/binary-formats.c",
CHECK-SAME: "segments":
CHECK-SAME: 4,40,100,true,true,false
CHECK-SAME: 4,42,0,false,false,false
CHECK-SAME: "summary":{"functions":{"count":1,"covered":1,"percent":100},
CHECK-SAME: "summary":{"branches":{"count":0,"covered":0,"notcovered":0,"percent":0},
CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
CHECK-SAME: "instantiations":{"count":1,"covered":1,"percent":100},
CHECK-SAME: "lines":{"count":1,"covered":1,"percent":100},
CHECK-SAME: "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}}}
CHECK-SAME: ],
CHECK-SAME: "functions":[
CHECK-SAME: {"count":100,"filenames":["/tmp/binary-formats.c"],"name":"main",
CHECK-SAME: {"branches":[],
CHECK-SAME: "count":100,"filenames":["/tmp/binary-formats.c"],"name":"main",
CHECK-SAME: "regions":
CHECK-SAME: 4,40,4,42,100,0,0,0
CHECK-SAME: }
CHECK-SAME: ],
CHECK-SAME: "totals":
CHECK-SAME: {"functions":{"count":1,"covered":1,"percent":100},
CHECK-SAME: {"branches":{"count":0,"covered":0,"notcovered":0,"percent":0},
CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
CHECK-SAME: "instantiations":{"count":1,"covered":1,"percent":100},
CHECK-SAME: "lines":{"count":1,"covered":1,"percent":100},
CHECK-SAME: "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}}}

Binary file not shown.

View File

@ -0,0 +1,206 @@
big_switch
# Func Hash:
13144136522122330070
# Num Counters:
17
# Counter Values:
1
32
32
1
0
1
1
11
11
1
1
15
15
1
1
2
2
boolean_operators
# Func Hash:
1245693242827665
# Num Counters:
14
# Counter Values:
1
100
34
1
66
66
17
1
34
17
33
33
50
33
boolop_loops
# Func Hash:
12402604614320574815
# Num Counters:
13
# Counter Values:
1
50
51
50
50
26
1
50
51
50
50
26
1
branch-c-general.c:static_func
# Func Hash:
18129
# Num Counters:
2
# Counter Values:
1
10
conditional_operator
# Func Hash:
54992
# Num Counters:
3
# Counter Values:
1
0
1
conditionals
# Func Hash:
4904767535850050386
# Num Counters:
13
# Counter Values:
1
100
50
50
33
33
16
99
100
99
99
100
1
do_fallthrough
# Func Hash:
8714614136504380050
# Num Counters:
4
# Counter Values:
1
10
2
8
early_exits
# Func Hash:
2880354649761471549
# Num Counters:
9
# Counter Values:
1
0
51
1
25
1
25
1
0
jumps
# Func Hash:
15051420506203462683
# Num Counters:
22
# Counter Values:
1
1
0
1
0
0
1
0
1
2
3
2
0
3
0
1
1
1
10
0
10
9
main
# Func Hash:
24
# Num Counters:
2
# Counter Values:
1
0
simple_loops
# Func Hash:
1245818015463121
# Num Counters:
4
# Counter Values:
1
100
100
75
switches
# Func Hash:
43242458792028222
# Num Counters:
19
# Counter Values:
1
1
1
15
7
1
0
2
2
3
3
4
4
0
4
4
5
1
0

Binary file not shown.

View File

@ -0,0 +1,84 @@
_Z4funcii
# Func Hash:
9495997393973228792
# Num Counters:
69
# Counter Values:
4
0
0
0
0
2
0
2
2
3
2
0
0
0
0
0
0
1
0
1
1
3
3
3
3
3
3
3
3
3
3
3
1
2
0
3
0
0
0
1
0
1
0
3
3
3
3
3
3
3
3
4
3
1
0
2
1
0
0
3
0
2
0
2
0
0
4
1
3
main
# Func Hash:
24
# Num Counters:
1
# Counter Values:
4

Binary file not shown.

View File

@ -0,0 +1,49 @@
_Z4funcii
# Func Hash:
2263559805428111017
# Num Counters:
19
# Counter Values:
3
1
0
1
0
1
0
1
0
1
0
0
0
0
0
0
0
0
0
_Z5func2ii
# Func Hash:
5552544297182115648
# Num Counters:
8
# Counter Values:
3
3
2
1
0
0
1
0
main
# Func Hash:
24
# Num Counters:
1
# Counter Values:
3

View File

@ -0,0 +1,28 @@
main
# Func Hash:
3890582504168513655
# Num Counters:
21
# Counter Values:
6
1
2
5
3
5
5
1
0
4
1
1
4
1
1
5
1
1
0
3
50

Binary file not shown.

View File

@ -0,0 +1,38 @@
_Z4funcIbEiT_
# Func Hash:
11045778961
# Num Counters:
2
# Counter Values:
1
1
_Z4funcIfEiT_
# Func Hash:
11045778961
# Num Counters:
2
# Counter Values:
1
0
_Z4funcIiEiT_
# Func Hash:
11045778961
# Num Counters:
2
# Counter Values:
1
0
main
# Func Hash:
185286008276329560
# Num Counters:
4
# Counter Values:
1
1
0
1

View File

@ -1,4 +1,4 @@
CHECK: {"expansions":
CHECK: {"branches":[],"expansions":
CHECK: "filenames":["{{[^"]+}}showExpansions.cpp",
CHECK: "source_region":[24,5,24,17,100,0,1,1],
CHECK: "filename":"{{[^"]+}}showExpansions.cpp",

View File

@ -0,0 +1,311 @@
// Test visualization of general branch constructs in C.
// RUN: llvm-profdata merge %S/Inputs/branch-c-general.proftext -o %t.profdata
// RUN: llvm-cov show --show-branches=count %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=REPORT
void simple_loops() {
int i;
for (i = 0; i < 100; ++i) { // CHECK: Branch ([[@LINE]]:15): [True: 100, False: 1]
}
while (i > 0) // CHECK: Branch ([[@LINE]]:10): [True: 100, False: 1]
i--;
do {} while (i++ < 75); // CHECK: Branch ([[@LINE]]:16): [True: 75, False: 1]
}
void conditionals() {
for (int i = 0; i < 100; ++i) {// CHECK: Branch ([[@LINE]]:19): [True: 100, False: 1]
if (i % 2) { // CHECK: Branch ([[@LINE]]:9): [True: 50, False: 50]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 50, False: 0]
} else if (i % 3) { // CHECK: Branch ([[@LINE]]:16): [True: 33, False: 17]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 33, False: 0]
} else {
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 16, False: 1]
}
// CHECK: Branch ([[@LINE+1]]:9): [Folded - Ignored]
if (1 && i) {} // CHECK: Branch ([[@LINE]]:14): [True: 99, False: 1]
if (0 || i) {} // CHECK: Branch ([[@LINE]]:9): [Folded - Ignored]
} // CHECK: Branch ([[@LINE-1]]:14): [True: 99, False: 1]
}
void early_exits() {
int i = 0;
if (i) {} // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 1]
while (i < 100) { // CHECK: Branch ([[@LINE]]:10): [True: 51, False: 0]
i++;
if (i > 50) // CHECK: Branch ([[@LINE]]:9): [True: 1, False: 50]
break;
if (i % 2) // CHECK: Branch ([[@LINE]]:9): [True: 25, False: 25]
continue;
}
if (i) {} // CHECK: Branch ([[@LINE]]:7): [True: 1, False: 0]
do {
if (i > 75) // CHECK: Branch ([[@LINE]]:9): [True: 1, False: 25]
return;
else
i++;
} while (i < 100); // CHECK: Branch ([[@LINE]]:12): [True: 25, False: 0]
if (i) {} // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 0]
}
void jumps() {
int i;
for (i = 0; i < 2; ++i) { // CHECK: Branch ([[@LINE]]:15): [True: 1, False: 0]
goto outofloop;
// Never reached -> no weights
if (i) {} // CHECK: Branch ([[@LINE]]:9): [True: 0, False: 0]
}
outofloop:
if (i) {} // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 1]
goto loop1;
while (i) { // CHECK: Branch ([[@LINE]]:10): [True: 0, False: 1]
loop1:
if (i) {} // CHECK: Branch ([[@LINE]]:9): [True: 0, False: 1]
}
goto loop2;
first:
second:
third:
i++;
if (i < 3) // CHECK: Branch ([[@LINE]]:7): [True: 2, False: 1]
goto loop2;
while (i < 3) { // CHECK: Branch ([[@LINE]]:10): [True: 0, False: 1]
loop2:
switch (i) {
case 0: // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 2]
goto first;
case 1: // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 2]
goto second;
case 2: // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 2]
goto third;
}
}
for (i = 0; i < 10; ++i) { // CHECK: Branch ([[@LINE]]:15): [True: 10, False: 1]
goto withinloop;
// never reached -> no weights
if (i) {} // CHECK: Branch ([[@LINE]]:9): [True: 0, False: 0]
withinloop:
if (i) {} // CHECK: Branch ([[@LINE]]:9): [True: 9, False: 1]
}
}
void switches() {
static int weights[] = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5};
// No cases -> no weights
switch (weights[0]) {
default: // CHECK: Branch ([[@LINE]]:3): [True: 1, False: 0]
break;
}
// CHECK: Branch ([[@LINE+1]]:63): [True: 15, False: 0]
for (int i = 0, len = sizeof(weights) / sizeof(weights[0]); i < len; ++i) {
switch (i[weights]) {
case 1: // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 14]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 0, False: 1]
// fallthrough
case 2: // CHECK: Branch ([[@LINE]]:5): [True: 2, False: 13]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 2, False: 1]
break;
case 3: // CHECK: Branch ([[@LINE]]:5): [True: 3, False: 12]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 3, False: 0]
continue;
case 4: // CHECK: Branch ([[@LINE]]:5): [True: 4, False: 11]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 4, False: 0]
switch (i) {
case 6 ... 9: // CHECK: Branch ([[@LINE]]:7): [True: 4, False: 0]
if (i) {} // CHECK: Branch ([[@LINE]]:13): [True: 4, False: 0]
continue;
}
default: // CHECK: Branch ([[@LINE]]:5): [True: 5, False: 10]
if (i == len - 1) // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 4]
return;
}
}
// Never reached -> no weights
if (weights[0]) {} // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 0]
}
void big_switch() {
for (int i = 0; i < 32; ++i) {// CHECK: Branch ([[@LINE]]:19): [True: 32, False: 1]
switch (1 << i) {
case (1 << 0): // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 31]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 0, False: 1]
// fallthrough
case (1 << 1): // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 31]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 1]
break;
case (1 << 2) ... (1 << 12):// CHECK: Branch ([[@LINE]]:5): [True: 11, False: 21]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 11, False: 0]
break;
// The branch for the large case range above appears after the case body.
case (1 << 13): // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 31]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 0]
break;
case (1 << 14) ... (1 << 28):// CHECK: Branch ([[@LINE]]:5): [True: 15, False: 17]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 15, False: 0]
break;
// The branch for the large case range above appears after the case body.
// CHECK: Branch ([[@LINE+1]]:5): [True: 1, False: 31]
case (1 << 29) ... ((1 << 29) + 1):
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 0]
break;
default: // CHECK: Branch ([[@LINE]]:5): [True: 2, False: 30]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 2, False: 0]
break;
}
}
}
void boolean_operators() {
int v; // CHECK: Branch ([[@LINE+1]]:19): [True: 100, False: 1]
for (int i = 0; i < 100; ++i) {
v = i % 3 || i; // CHECK: Branch ([[@LINE]]:9): [True: 66, False: 34]
// CHECK: Branch ([[@LINE-1]]:18): [True: 33, False: 1]
v = i % 3 && i; // CHECK: Branch ([[@LINE]]:9): [True: 66, False: 34]
// CHECK: Branch ([[@LINE-1]]:18): [True: 66, False: 0]
v = i % 3 || i % 2 || i; // CHECK: Branch ([[@LINE]]:9): [True: 66, False: 34]
// CHECK: Branch ([[@LINE-1]]:18): [True: 17, False: 17]
v = i % 2 && i % 3 && i; // CHECK: Branch ([[@LINE-2]]:27): [True: 16, False: 1]
} // CHECK: Branch ([[@LINE-1]]:9): [True: 50, False: 50]
// CHECK: Branch ([[@LINE-2]]:18): [True: 33, False: 17]
} // CHECK: Branch ([[@LINE-3]]:27): [True: 33, False: 0]
void boolop_loops() {
int i = 100;
while (i && i > 50) // CHECK: Branch ([[@LINE]]:10): [True: 51, False: 0]
i--; // CHECK: Branch ([[@LINE-1]]:15): [True: 50, False: 1]
while ((i % 2) || (i > 0)) // CHECK: Branch ([[@LINE]]:10): [True: 25, False: 26]
i--; // CHECK: Branch ([[@LINE-1]]:21): [True: 25, False: 1]
for (i = 100; i && i > 50; --i); // CHECK: Branch ([[@LINE]]:17): [True: 51, False: 0]
// CHECK: Branch ([[@LINE-1]]:22): [True: 50, False: 1]
for (; (i % 2) || (i > 0); --i); // CHECK: Branch ([[@LINE]]:10): [True: 25, False: 26]
// CHECK: Branch ([[@LINE-1]]:21): [True: 25, False: 1]
}
void conditional_operator() {
int i = 100;
int j = i < 50 ? i : 1; // CHECK: Branch ([[@LINE]]:11): [True: 0, False: 1]
int k = i ?: 0; // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 0]
}
void do_fallthrough() {
for (int i = 0; i < 10; ++i) {// CHECK: Branch ([[@LINE]]:19): [True: 10, False: 1]
int j = 0;
do {
// The number of exits out of this do-loop via the break statement
// exceeds the counter value for the loop (which does not include the
// fallthrough count). Make sure that does not violate any assertions.
if (i < 8) break; // CHECK: Branch ([[@LINE]]:11): [True: 8, False: 4]
j++;
} while (j < 2); // CHECK: Branch ([[@LINE]]:14): [True: 2, False: 2]
}
}
static void static_func() {
for (int i = 0; i < 10; ++i) {// CHECK: Branch ([[@LINE]]:19): [True: 10, False: 1]
}
}
int main(int argc, const char *argv[]) {
simple_loops();
conditionals();
early_exits();
jumps();
switches();
big_switch();
boolean_operators();
boolop_loops();
conditional_operator();
do_fallthrough();
static_func();
extern void __llvm_profile_write_file();
__llvm_profile_write_file();
return 0;
}
// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover
// REPORT-NEXT: ---
// REPORT-NEXT: simple_loops 8 0 100.00% 9 0 100.00% 6 0 100.00%
// REPORT-NEXT: conditionals 24 0 100.00% 15 0 100.00% 16 2 87.50%
// REPORT-NEXT: early_exits 20 4 80.00% 25 3 88.00% 16 6 62.50%
// REPORT-NEXT: jumps 39 12 69.23% 48 4 91.67% 26 9 65.38%
// REPORT-NEXT: switches 28 5 82.14% 38 5 86.84% 30 9 70.00%
// REPORT-NEXT: big_switch 25 1 96.00% 32 0 100.00% 30 6 80.00%
// REPORT-NEXT: boolean_operators 16 0 100.00% 13 0 100.00% 22 2 90.91%
// REPORT-NEXT: boolop_loops 19 0 100.00% 14 0 100.00% 16 2 87.50%
// REPORT-NEXT: conditional_operator 4 2 50.00% 8 1 87.50% 4 2 50.00%
// REPORT-NEXT: do_fallthrough 9 0 100.00% 12 0 100.00% 6 0 100.00%
// REPORT-NEXT: main 1 0 100.00% 16 0 100.00% 0 0 0.00%
// REPORT-NEXT: c-general.c:static_func 4 0 100.00% 4 0 100.00% 2 0 100.00%
// REPORT-NEXT: ---
// REPORT-NEXT: TOTAL 197 24 87.82% 234 13 94.44% 174 38 78.16%
// Test file-level report.
// RUN: llvm-profdata merge %S/Inputs/branch-c-general.proftext -o %t.profdata
// RUN: llvm-cov report %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=FILEREPORT
// FILEREPORT: TOTAL{{.*}}174 38 78.16%
// Test color True/False output.
// RUN: llvm-cov show --use-color --show-branches=count %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=USECOLOR
// USECOLOR: Branch ({{[0-9]+}}:7): {{.*}}: 0, {{.*}}0]
// Test html output.
// RUN: llvm-cov show --show-branch-summary --show-branches=count %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s -format html -o %t.html.dir
// RUN: FileCheck -check-prefix=HTML -input-file=%t.html.dir/coverage/tmp/branch-c-general.c.html %s
// HTML-COUNT-89: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>
// HTML-NOT: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>
// RUN: FileCheck -check-prefix HTML-INDEX -input-file %t.html.dir/index.html %s
// HTML-INDEX-LABEL: <table>
// HTML-INDEX: <td class='column-entry-bold'>Filename</td>
// HTML-INDEX: <td class='column-entry-bold'>Function Coverage</td>
// HTML-INDEX: <td class='column-entry-bold'>Line Coverage</td>
// HTML-INDEX: <td class='column-entry-bold'>Region Coverage</td>
// HTML-INDEX: <td class='column-entry-bold'>Branch Coverage</td>
// HTML-INDEX: <a href='coverage{{.*}}branch-c-general.c.html'{{.*}}branch-c-general.c</a>
// HTML-INDEX: <td class='column-entry-green'>
// HTML-INDEX: 100.00% (12/12)
// HTML-INDEX: <td class='column-entry-yellow'>
// HTML-INDEX: 94.44% (221/234)
// HTML-INDEX: <td class='column-entry-yellow'>
// HTML-INDEX: 87.82% (173/197)
// HTML-INDEX: <td class='column-entry-red'>
// HTML-INDEX: 78.16% (136/174)
// HTML-INDEX: <tr class='light-row-bold'>
// HTML-INDEX: Totals

View File

@ -0,0 +1,49 @@
// RUN: llvm-profdata merge %S/Inputs/branch-showBranchPercentage.proftext -o %t.profdata
// RUN: llvm-cov export --format=text %S/Inputs/branch-showBranchPercentage.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-showBranchPercentage.c | FileCheck %s
// CHECK: "branches":
// CHECK: 14,7,14,15,1,5,0,0,4
// CHECK: 27,8,27,14,1,4,0,0,4
// CHECK: 27,18,27,24,0,1,0,0,4
// CHECK: 27,29,27,36,0,5,0,0,4
// CHECK: 27,40,27,46,2,3,0,0,4
// CHECK: 30,8,30,14,4,1,0,0,4
// CHECK: 30,18,30,24,0,1,0,0,4
// CHECK: 32,8,32,14,4,1,0,0,4
// CHECK: 32,18,32,24,1,3,0,0,4
// CHECK: 34,15,34,20,1,5,0,0,4
// CHECK: 41,5,41,11,1,4,0,0,4
// CHECK: 43,5,43,11,1,4,0,0,4
// CHECK: 45,5,45,11,0,5,0,0,4
// CHECK: 47,5,47,12,3,2,0,0,4
// CHECK: 53,12,53,20,50,5,0,0,4
// CHECK: {"count":30,"covered":26,"notcovered":4,"percent":86.666666666666671}
// Check recursive macro-expansions.
// RUN: llvm-profdata merge %S/Inputs/branch-macros.proftext -o %t.profdata
// RUN: llvm-cov export --format=text %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-macros.c | FileCheck %s -check-prefix=MACROS
// MACROS: "branches":
// MACROS: 27,10,27,11,0,3,0,0,4
// MACROS: 27,15,27,16,0,0,0,0,4
// MACROS: 27,20,27,21,0,0,0,0,4
// MACROS: 27,25,27,26,0,0,0,0,4
// MACROS: 27,30,27,31,0,0,0,0,4
// MACROS: 15,5,23,1,2,1,0,4
// MACROS: 6,15,6,23,0,1,2,0,4
// MACROS: 5,15,5,23,1,2,7,0,4
// MACROS: 6,15,6,23,0,1,8,0,4
// MACROS: 5,15,5,23,1,2,12,0,4
// MACROS: 6,15,6,23,0,1,13,0,4
// MACROS: 5,15,5,23,1,2,16,0,4
// MACROS: 6,15,6,23,0,1,17,0,4
// MACROS: 5,15,5,23,1,2,19,0,4
// MACROS: 6,15,6,23,0,1,20,0,4
// MACROS: 5,15,5,23,1,2,11,0,4
// MACROS: 6,15,6,23,0,1,12,0,4
// MACROS: 5,15,5,23,1,2,8,0,4
// MACROS: 6,15,6,23,0,1,9,0,4
// MACROS: 8,15,8,38,1,2,2,0,4
// MACROS: {"count":40,"covered":24,"notcovered":16,"percent":60}

View File

@ -0,0 +1,73 @@
// RUN: llvm-profdata merge %S/Inputs/branch-showBranchPercentage.proftext -o %t.profdata
// RUN: llvm-cov export --format=lcov %S/Inputs/branch-showBranchPercentage.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-showBranchPercentage.c | FileCheck %s
// CHECK-DAG: BRDA:14,0,0,1
// CHECK-DAG: BRDA:14,0,1,5
// CHECK-DAG: BRDA:27,0,0,1
// CHECK-DAG: BRDA:27,0,1,4
// CHECK-DAG: BRDA:27,1,2,0
// CHECK-DAG: BRDA:27,1,3,1
// CHECK-DAG: BRDA:27,2,4,0
// CHECK-DAG: BRDA:27,2,5,5
// CHECK-DAG: BRDA:27,3,6,2
// CHECK-DAG: BRDA:27,3,7,3
// CHECK-DAG: BRDA:30,0,0,4
// CHECK-DAG: BRDA:30,0,1,1
// CHECK-DAG: BRDA:30,1,2,0
// CHECK-DAG: BRDA:30,1,3,1
// CHECK-DAG: BRDA:32,0,0,4
// CHECK-DAG: BRDA:32,0,1,1
// CHECK-DAG: BRDA:32,1,2,1
// CHECK-DAG: BRDA:32,1,3,3
// CHECK-DAG: BRDA:34,0,0,1
// CHECK-DAG: BRDA:34,0,1,5
// CHECK-DAG: BRDA:41,0,0,1
// CHECK-DAG: BRDA:41,0,1,4
// CHECK-DAG: BRDA:43,0,0,1
// CHECK-DAG: BRDA:43,0,1,4
// CHECK-DAG: BRDA:45,0,0,0
// CHECK-DAG: BRDA:45,0,1,5
// CHECK-DAG: BRDA:47,0,0,3
// CHECK-DAG: BRDA:47,0,1,2
// CHECK-DAG: BRDA:53,0,0,50
// CHECK-DAG: BRDA:53,0,1,5
// CHECK-NOT: BRDA
// CHECK: BRF:30
// CHECK: BFH:26
// Check recursive macro-expansions.
// RUN: llvm-profdata merge %S/Inputs/branch-macros.proftext -o %t.profdata
// RUN: llvm-cov export --format=lcov %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-macros.cpp | FileCheck %s -check-prefix=MACROS
// MACROS-COUNT-4: BRDA:17
// MACROS-NOT: BRDA:17
// MACROS-COUNT-4: BRDA:19
// MACROS-NOT: BRDA:19
// MACROS-COUNT-4: BRDA:21
// MACROS-NOT: BRDA:21
// MACROS-COUNT-4: BRDA:23
// MACROS-NOT: BRDA:23
// MACROS-COUNT-4: BRDA:25
// MACROS-NOT: BRDA:25
// MACROS: BRDA:27,0,0,0
// MACROS: BRDA:27,0,1,3
// MACROS: BRDA:27,1,2,-
// MACROS: BRDA:27,1,3,-
// MACROS: BRDA:27,2,4,-
// MACROS: BRDA:27,2,5,-
// MACROS: BRDA:27,3,6,-
// MACROS: BRDA:27,3,7,-
// MACROS: BRDA:27,4,8,-
// MACROS: BRDA:27,4,9,-
// MACROS-COUNT-10: BRDA:37
// MACROS-NOT: BRDA:37
// MACROS-NOT: BRDA
// MACROS: BRF:40
// MACROS: BFH:24

View File

@ -0,0 +1,90 @@
// RUN: llvm-profdata merge %S/Inputs/branch-logical-mixed.proftext -o %t.profdata
// RUN: llvm-cov show --show-branches=count %S/Inputs/branch-logical-mixed.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-logical-mixed.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=REPORT
#include <stdio.h>
#include <stdlib.h>
void func(int a, int b) {
bool b0 = a <= b;
bool b1 = a == b;
bool b2 = a >= b;
bool b3 = a < b;
bool b4 = a > b;
bool b5 = a != b;
bool c = b0 && // CHECK: Branch ([[@LINE]]:12): [True: 3, False: 1]
b1 && // CHECK: Branch ([[@LINE]]:12): [True: 2, False: 1]
b2 && // CHECK: Branch ([[@LINE]]:12): [True: 2, False: 0]
b3 && // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 2]
b4 && // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0]
b5; // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0]
bool d = b0 || // CHECK: Branch ([[@LINE]]:12): [True: 3, False: 1]
b1 || // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 1]
b2 || // CHECK: Branch ([[@LINE]]:12): [True: 1, False: 0]
b3 || // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0]
b4 || // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0]
b5; // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0]
bool e = (b0 && // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 1]
b5) || // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2]
(b1 && // CHECK: Branch ([[@LINE]]:13): [True: 2, False: 1]
b4) || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 2]
(b2 && // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 0]
b3) || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 3]
(b3 && // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 3]
b2) || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 0]
(b4 && // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2]
b1) || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 1]
(b5 && // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2]
b0); // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 1]
bool f = (b0 || // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 1]
b5) && // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 0]
(b1 || // CHECK: Branch ([[@LINE]]:13): [True: 2, False: 2]
b4) && // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 1]
(b2 || // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 0]
b3) && // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 0]
(b3 || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 3]
b2) && // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 0]
(b4 || // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2]
b1) && // CHECK: Branch ([[@LINE]]:13): [True: 2, False: 0]
(b5 || // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2]
b0); // CHECK: Branch ([[@LINE]]:13): [True: 2, False: 0]
if (c) // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 4]
printf("case0\n");
else
printf("case1\n");
if (d) // CHECK: Branch ([[@LINE]]:7): [True: 4, False: 0]
printf("case2\n");
else
printf("case3\n");
if (e) // CHECK: Branch ([[@LINE]]:7): [True: 1, False: 3]
printf("case4\n");
else
printf("case5\n");
if (f) // CHECK: Branch ([[@LINE]]:7): [True: 3, False: 1]
printf("case6\n");
else
printf("case7\n");
}
extern "C" { extern void __llvm_profile_write_file(void); }
int main(int argc, char *argv[])
{
func(atoi(argv[1]), atoi(argv[2]));
__llvm_profile_write_file();
return 0;
}
// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover
// REPORT-NEXT: ---
// REPORT-NEXT: _Z4funcii 77 9 88.31% 68 10 85.29% 80 32 60.00%
// REPORT-NEXT: main 1 0 100.00% 5 0 100.00% 0 0 0.00%
// REPORT-NEXT: ---
// REPORT-NEXT: TOTAL 78 9 88.46% 73 10 86.30% 80 32 60.00%

View File

@ -0,0 +1,60 @@
// RUN: llvm-profdata merge %S/Inputs/branch-macros.proftext -o %t.profdata
// RUN: llvm-cov show --show-expansions --show-branches=count %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=REPORT
#define COND1 (a == b)
#define COND2 (a != b)
#define COND3 (COND1 && COND2)
#define COND4 (COND3 ? COND2 : COND1)
#define MACRO1 COND3
#define MACRO2 MACRO1
#define MACRO3 MACRO2
#include <stdlib.h>
bool func(int a, int b) {
bool c = COND1 && COND2; // CHECK: | | | Branch ([[@LINE-12]]:15): [True: 1, False: 2]
// CHECK: | | | Branch ([[@LINE-12]]:15): [True: 0, False: 1]
bool d = COND3; // CHECK: | | | | | Branch ([[@LINE-14]]:15): [True: 1, False: 2]
// CHECK: | | | | | Branch ([[@LINE-14]]:15): [True: 0, False: 1]
bool e = MACRO1; // CHECK: | | | | | | | Branch ([[@LINE-16]]:15): [True: 1, False: 2]
// CHECK: | | | | | | | Branch ([[@LINE-16]]:15): [True: 0, False: 1]
bool f = MACRO2; // CHECK: | | | | | | | | | Branch ([[@LINE-18]]:15): [True: 1, False: 2]
// CHECK: | | | | | | | | | Branch ([[@LINE-18]]:15): [True: 0, False: 1]
bool g = MACRO3; // CHECK: | | | | | | | | | | | Branch ([[@LINE-20]]:15): [True: 1, False: 2]
// CHECK: | | | | | | | | | | | Branch ([[@LINE-20]]:15): [True: 0, False: 1]
return c && d && e && f && g;
// CHECK: | Branch ([[@LINE-1]]:10): [True: 0, False: 3]
// CHECK: | Branch ([[@LINE-2]]:15): [True: 0, False: 0]
// CHECK: | Branch ([[@LINE-3]]:20): [True: 0, False: 0]
// CHECK: | Branch ([[@LINE-4]]:25): [True: 0, False: 0]
// CHECK: | Branch ([[@LINE-5]]:30): [True: 0, False: 0]
}
bool func2(int a, int b) {
bool h = MACRO3 || COND4; // CHECK: | | | | | | | | | | | Branch ([[@LINE-32]]:15): [True: 1, False: 2]
// CHECK: | | | | | | | | | | | Branch ([[@LINE-32]]:15): [True: 0, False: 1]
// CHECK: | | | | | | | Branch ([[@LINE-34]]:15): [True: 1, False: 2]
// CHECK: | | | | | | | Branch ([[@LINE-34]]:15): [True: 0, False: 1]
// CHECK: | | | Branch ([[@LINE-33]]:15): [True: 1, False: 2]
return h;
}
extern "C" { extern void __llvm_profile_write_file(void); }
int main(int argc, char *argv[])
{
func(atoi(argv[1]), atoi(argv[2]));
func2(atoi(argv[1]), atoi(argv[2]));
__llvm_profile_write_file();
return 0;
}
// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover
// REPORT-NEXT: ---
// REPORT-NEXT: _Z4funcii 28 4 85.71% 18 0 100.00% 30 14 53.33%
// REPORT-NEXT: _Z5func2ii 13 1 92.31% 8 0 100.00% 10 2 80.00%
// REPORT-NEXT: main 1 0 100.00% 6 0 100.00% 0 0 0.00%
// REPORT-NEXT: ---
// REPORT-NEXT: TOTAL 42 5 88.10% 32 0 100.00% 40 16 60.00%

View File

@ -0,0 +1,25 @@
// RUN: llvm-profdata merge %S/Inputs/branch-c-general.proftext -o %t.profdata
// RUN: llvm-cov show %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-c-general.c | FileCheck %s
// RUN: llvm-cov report %S/Inputs/branch-c-general.o32l --show-branch-summary=false -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %S/branch-c-general.c | FileCheck %s -check-prefix=REPORT
// CHECK-NOT: | Branch
// REPORT: Name Regions Miss Cover Lines Miss Cover
// REPORT-NOT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover
// REPORT: ---
// REPORT-NOT: simple_loops 8 0 100.00% 9 0 100.00% 6 0 100.00%
// REPORT-NOT: conditionals 24 0 100.00% 15 0 100.00% 16 2 87.50%
// REPORT-NOT: early_exits 20 4 80.00% 25 2 92.00% 16 6 62.50%
// REPORT-NOT: jumps 39 12 69.23% 48 2 95.83% 26 9 65.38%
// REPORT-NOT: switches 28 5 82.14% 38 4 89.47% 30 9 70.00%
// REPORT-NOT: big_switch 25 1 96.00% 32 0 100.00% 30 6 80.00%
// REPORT-NOT: boolean_operators 16 0 100.00% 13 0 100.00% 22 2 90.91%
// REPORT-NOT: boolop_loops 19 0 100.00% 14 0 100.00% 16 2 87.50%
// REPORT-NOT: conditional_operator 4 2 50.00% 8 0 100.00% 4 2 50.00%
// REPORT-NOT: do_fallthrough 9 0 100.00% 12 0 100.00% 6 0 100.00%
// REPORT-NOT: main 1 0 100.00% 16 0 100.00% 0 0 0.00%
// REPORT-NOT: c-general.c:static_func 4 0 100.00% 4 0 100.00% 2 0 100.00%
// REPORT: TOTAL 197 24 87.82% 234 13 94.44%
// REPORT-NOT: TOTAL 197 24 87.82% 234 13 94.44% 174 38 78.16%

View File

@ -0,0 +1,77 @@
// Test visualization of branch taken percentages
// RUN: llvm-profdata merge %S/Inputs/branch-showBranchPercentage.proftext -o %t.profdata
// RUN: llvm-cov show --show-branches=percent %S/Inputs/branch-showBranchPercentage.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
#include <stdio.h>
#include <stdlib.h>
extern void __llvm_profile_write_file(void);
int main(int argc, char *argv[])
{
int i = 0;
if (argc < 3) // CHECK: Branch ([[@LINE]]:7): [True: 16.67%, False: 83.33%]
{
__llvm_profile_write_file();
return 0;
}
int a = atoi(argv[1]);
int b = atoi(argv[2]);
// CHECK: Branch ([[@LINE+4]]:8): [True: 20.00%, False: 80.00%]
// CHECK: Branch ([[@LINE+3]]:18): [True: 0.00%, False: 100.00%]
// CHECK: Branch ([[@LINE+2]]:29): [True: 0.00%, False: 100.00%]
// CHECK: Branch ([[@LINE+1]]:40): [True: 40.00%, False: 60.00%]
if ((a == 0 && b == 2) || b == 34 || a == b)
printf("case1\n");
b = (a != 0 || a == 2) ? b : b+2; // CHECK: Branch ([[@LINE]]:8): [True: 80.00%, False: 20.00%]
// CHECK: Branch ([[@LINE-1]]:18): [True: 0.00%, False: 100.00%]
b = (a != 0 && a == 1); // CHECK: Branch ([[@LINE]]:8): [True: 80.00%, False: 20.00%]
// CHECK: Branch ([[@LINE-1]]:18): [True: 25.00%, False: 75.00%]
for (i = 0; i < b; i++) { a = 2 + b + b; }
// CHECK: Branch ([[@LINE-1]]:15): [True: 16.67%, False: 83.33%]
b = a;
switch (a)
{
case 0: // CHECK: Branch ([[@LINE]]:5): [True: 20.00%, False: 80.00%]
printf("case0\n");
case 2: // CHECK: Branch ([[@LINE]]:5): [True: 20.00%, False: 80.00%]
printf("case2\n");
case 3: // CHECK: Branch ([[@LINE]]:5): [True: 0.00%, False: 100.00%]
printf("case3\n");
default: break; // CHECK: Branch ([[@LINE]]:5): [True: 60.00%, False: 40.00%]
}
i = 0;
do {
printf("loop\n");
} while (i++ < 10); // CHECK: Branch ([[@LINE]]:12): [True: 90.91%, False: 9.09%]
__llvm_profile_write_file();
return b;
}
// RUN: llvm-profdata merge %S/Inputs/branch-showBranchPercentage.proftext -o %t.profdata
// RUN: llvm-cov show --show-branches=percent %S/Inputs/branch-showBranchPercentage.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s -format html -o %t.html.dir
// Test html output.
// RUN: FileCheck -check-prefix=HTML -input-file=%t.html.dir/coverage/tmp/branch-showBranchPercentage.c.html %s
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}16.67%,{{.*}}83.33%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}20.00%,{{.*}}80.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}0.00%,{{.*}}100.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}0.00%,{{.*}}100.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}40.00%,{{.*}}60.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}80.00%,{{.*}}20.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}0.00%,{{.*}}100.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}80.00%,{{.*}}20.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}25.00%,{{.*}}75.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}16.67%,{{.*}}83.33%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}20.00%,{{.*}}80.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}20.00%,{{.*}}80.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}0.00%,{{.*}}100.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}60.00%,{{.*}}40.00%]

View File

@ -0,0 +1,47 @@
// RUN: llvm-profdata merge %S/Inputs/branch-templates.proftext -o %t.profdata
// RUN: llvm-cov show --show-expansions --show-branches=count %S/Inputs/branch-templates.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-templates.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=REPORT
#include <stdio.h>
template<typename T>
void unused(T x) {
return;
}
template<typename T>
int func(T x) {
if(x) // CHECK: | Branch ([[@LINE]]:6): [True: 0, False: 1]
return 0; // CHECK: | Branch ([[@LINE-1]]:6): [True: 1, False: 0]
else // CHECK: | Branch ([[@LINE-2]]:6): [True: 0, False: 1]
return 1;
int j = 1;
}
// CHECK-LABEL: _Z4funcIiEiT_:
// CHECK: | | Branch ([[@LINE-8]]:6): [True: 0, False: 1]
// CHECK-LABEL: _Z4funcIbEiT_:
// CHECK: | | Branch ([[@LINE-10]]:6): [True: 1, False: 0]
// CHECK-LABEL: _Z4funcIfEiT_:
// CHECK: | | Branch ([[@LINE-12]]:6): [True: 0, False: 1]
extern "C" { extern void __llvm_profile_write_file(void); }
int main() {
if (func<int>(0)) // CHECK: | Branch ([[@LINE]]:7): [True: 1, False: 0]
printf("case1\n");
if (func<bool>(true)) // CHECK: | Branch ([[@LINE]]:7): [True: 0, False: 1]
printf("case2\n");
if (func<float>(0.0)) // CHECK: | Branch ([[@LINE]]:7): [True: 1, False: 0]
printf("case3\n");
__llvm_profile_write_file();
return 0;
}
// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover
// REPORT-NEXT: ---
// REPORT-NEXT: main 7 1 85.71% 10 1 90.00% 6 3 50.00%
// REPORT-NEXT: _Z4funcIiEiT_ 5 2 60.00% 7 3 57.14% 2 1 50.00%
// REPORT-NEXT: _Z4funcIbEiT_ 5 2 60.00% 7 4 42.86% 2 1 50.00%
// REPORT-NEXT: _Z4funcIfEiT_ 5 2 60.00% 7 3 57.14% 2 1 50.00%
// REPORT-NEXT: ---
// REPORT-NEXT: TOTAL 22 7 68.18% 31 11 64.52% 12 6 50.00%

View File

@ -4,7 +4,7 @@
# Ignore all header files.
RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \
RUN: -path-equivalence=/tmp,%S/Inputs -ignore-filename-regex='.*\.h$' \
RUN: %S/Inputs/sources_specified/main.covmapping \
RUN: %S/Inputs/sources_specified/main.covmapping --show-branch-summary=false \
RUN: | FileCheck -check-prefix=REPORT_IGNORE_HEADERS %s
REPORT_IGNORE_HEADERS-NOT: {{.*}}dec.h{{.*}}
@ -15,7 +15,7 @@ REPORT_IGNORE_HEADERS: {{^}}TOTAL 1{{.*}}100.00%{{$}}
# Ignore all files from "extra" directory.
RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \
RUN: -path-equivalence=/tmp,%S/Inputs -ignore-filename-regex='.*extra[/\\].*' \
RUN: %S/Inputs/sources_specified/main.covmapping \
RUN: %S/Inputs/sources_specified/main.covmapping --show-branch-summary=false \
RUN: | FileCheck -check-prefix=REPORT_IGNORE_DIR %s
REPORT_IGNORE_DIR-NOT: {{.*}}extra{{[/\\]}}dec.h{{.*}}
@ -27,7 +27,7 @@ REPORT_IGNORE_DIR: {{^}}TOTAL 5{{.*}}90.00%{{$}}
# Ignore all files from "extra" directory even when SOURCES specified.
RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \
RUN: -path-equivalence=/tmp,%S/Inputs -ignore-filename-regex='.*extra[/\\].*' \
RUN: %S/Inputs/sources_specified/main.covmapping \
RUN: %S/Inputs/sources_specified/main.covmapping --show-branch-summary=false \
RUN: %S/Inputs/sources_specified/extra %S/Inputs/sources_specified/abs.h \
RUN: | FileCheck -check-prefix=REPORT_IGNORE_DIR_WITH_SOURCES %s

View File

@ -88,6 +88,12 @@ private:
ArrayRef<ExpansionRecord> Expansions,
const CoverageMapping &Coverage);
/// Create source views for the branches of the view.
void attachBranchSubViews(SourceCoverageView &View, StringRef SourceName,
ArrayRef<CountedRegion> Branches,
const MemoryBuffer &File,
CoverageData &CoverageInfo);
/// Create the source view of a particular function.
std::unique_ptr<SourceCoverageView>
createFunctionView(const FunctionRecord &Function,
@ -268,15 +274,45 @@ void CodeCoverageTool::attachExpansionSubViews(
if (!SourceBuffer)
continue;
auto SubViewBranches = ExpansionCoverage.getBranches();
auto SubViewExpansions = ExpansionCoverage.getExpansions();
auto SubView =
SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
ViewOpts, std::move(ExpansionCoverage));
attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
attachBranchSubViews(*SubView, Expansion.Function.Name, SubViewBranches,
SourceBuffer.get(), ExpansionCoverage);
View.addExpansion(Expansion.Region, std::move(SubView));
}
}
void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View,
StringRef SourceName,
ArrayRef<CountedRegion> Branches,
const MemoryBuffer &File,
CoverageData &CoverageInfo) {
if (!ViewOpts.ShowBranchCounts && !ViewOpts.ShowBranchPercents)
return;
const auto *NextBranch = Branches.begin();
const auto *EndBranch = Branches.end();
// Group branches that have the same line number into the same subview.
while (NextBranch != EndBranch) {
std::vector<CountedRegion> ViewBranches;
unsigned CurrentLine = NextBranch->LineStart;
while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart)
ViewBranches.push_back(*NextBranch++);
if (!ViewBranches.empty()) {
auto SubView = SourceCoverageView::create(SourceName, File, ViewOpts,
std::move(CoverageInfo));
View.addBranch(CurrentLine, ViewBranches, std::move(SubView));
}
}
}
std::unique_ptr<SourceCoverageView>
CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
const CoverageMapping &Coverage) {
@ -287,11 +323,14 @@ CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
if (!SourceBuffer)
return nullptr;
auto Branches = FunctionCoverage.getBranches();
auto Expansions = FunctionCoverage.getExpansions();
auto View = SourceCoverageView::create(DC.demangle(Function.Name),
SourceBuffer.get(), ViewOpts,
std::move(FunctionCoverage));
attachExpansionSubViews(*View, Expansions, Coverage);
attachBranchSubViews(*View, DC.demangle(Function.Name), Branches,
SourceBuffer.get(), FunctionCoverage);
return View;
}
@ -306,10 +345,13 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile,
if (FileCoverage.empty())
return nullptr;
auto Branches = FileCoverage.getBranches();
auto Expansions = FileCoverage.getExpansions();
auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
ViewOpts, std::move(FileCoverage));
attachExpansionSubViews(*View, Expansions, Coverage);
attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(),
FileCoverage);
if (!ViewOpts.ShowFunctionInstantiations)
return View;
@ -326,9 +368,12 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile,
if (Function->ExecutionCount > 0) {
auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
auto SubViewExpansions = SubViewCoverage.getExpansions();
auto SubViewBranches = SubViewCoverage.getBranches();
SubView = SourceCoverageView::create(
Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
attachBranchSubViews(*SubView, SourceFile, SubViewBranches,
SourceBuffer.get(), SubViewCoverage);
}
unsigned FileID = Function->CountedRegions.front().FileID;
@ -645,6 +690,11 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
cl::desc("Show region statistics in summary table"),
cl::init(true));
cl::opt<bool> BranchSummary(
"show-branch-summary", cl::Optional,
cl::desc("Show branch condition statistics in summary table"),
cl::init(true));
cl::opt<bool> InstantiationSummary(
"show-instantiation-summary", cl::Optional,
cl::desc("Show instantiation statistics in summary table"));
@ -788,6 +838,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
::exit(0);
}
ViewOpts.ShowBranchSummary = BranchSummary;
ViewOpts.ShowRegionSummary = RegionSummary;
ViewOpts.ShowInstantiationSummary = InstantiationSummary;
ViewOpts.ExportSummaryOnly = SummaryOnly;
@ -822,6 +873,15 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
cl::desc("Show the execution counts for each region"),
cl::cat(ViewCategory));
cl::opt<CoverageViewOptions::BranchOutputType> ShowBranches(
"show-branches", cl::Optional,
cl::desc("Show coverage for branch conditions"), cl::cat(ViewCategory),
cl::values(clEnumValN(CoverageViewOptions::BranchOutputType::Count,
"count", "Show True/False counts"),
clEnumValN(CoverageViewOptions::BranchOutputType::Percent,
"percent", "Show True/False percent")),
cl::init(CoverageViewOptions::BranchOutputType::Off));
cl::opt<bool> ShowBestLineRegionsCounts(
"show-line-counts-or-regions", cl::Optional,
cl::desc("Show the execution counts for each line, or the execution "
@ -865,6 +925,10 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
!ShowRegions || ShowBestLineRegionsCounts;
ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
ViewOpts.ShowExpandedRegions = ShowExpansions;
ViewOpts.ShowBranchCounts =
ShowBranches == CoverageViewOptions::BranchOutputType::Count;
ViewOpts.ShowBranchPercents =
ShowBranches == CoverageViewOptions::BranchOutputType::Percent;
ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
ViewOpts.TabSize = TabSize;

View File

@ -18,6 +18,8 @@
// -- Export: dict => Json representation of one CoverageMapping
// -- Files: array => List of objects describing coverage for files
// -- File: dict => Coverage for a single file
// -- Branches: array => List of Branches in the file
// -- Branch: dict => Describes a branch of the file with counters
// -- Segments: array => List of Segments contained in the file
// -- Segment: dict => Describes a segment of the file with a counter
// -- Expansions: array => List of expansion records
@ -25,10 +27,13 @@
// -- CountedRegion: dict => The region to be expanded
// -- TargetRegions: array => List of Regions in the expansion
// -- CountedRegion: dict => Single Region in the expansion
// -- Branches: array => List of Branches in the expansion
// -- Branch: dict => Describes a branch in expansion and counters
// -- Summary: dict => Object summarizing the coverage for this file
// -- LineCoverage: dict => Object summarizing line coverage
// -- FunctionCoverage: dict => Object summarizing function coverage
// -- RegionCoverage: dict => Object summarizing region coverage
// -- BranchCoverage: dict => Object summarizing branch coverage
// -- Functions: array => List of objects describing coverage for functions
// -- Function: dict => Coverage info for a single function
// -- Filenames: array => List of filenames that the function relates to
@ -37,6 +42,7 @@
// -- FunctionCoverage: dict => Object summarizing function coverage
// -- InstantiationCoverage: dict => Object summarizing inst. coverage
// -- RegionCoverage: dict => Object summarizing region coverage
// -- BranchCoverage: dict => Object summarizing branch coverage
//
//===----------------------------------------------------------------------===//
@ -84,6 +90,14 @@ json::Array renderRegion(const coverage::CountedRegion &Region) {
int64_t(Region.Kind)});
}
json::Array renderBranch(const coverage::CountedRegion &Region) {
return json::Array(
{Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd,
clamp_uint64_to_int64(Region.ExecutionCount),
clamp_uint64_to_int64(Region.FalseExecutionCount), Region.FileID,
Region.ExpandedFileID, int64_t(Region.Kind)});
}
json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) {
json::Array RegionArray;
for (const auto &Region : Regions)
@ -91,13 +105,49 @@ json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) {
return RegionArray;
}
json::Object renderExpansion(const coverage::ExpansionRecord &Expansion) {
json::Array renderBranchRegions(ArrayRef<coverage::CountedRegion> Regions) {
json::Array RegionArray;
for (const auto &Region : Regions)
if (!Region.Folded)
RegionArray.push_back(renderBranch(Region));
return RegionArray;
}
std::vector<llvm::coverage::CountedRegion>
collectNestedBranches(const coverage::CoverageMapping &Coverage,
ArrayRef<llvm::coverage::ExpansionRecord> Expansions) {
std::vector<llvm::coverage::CountedRegion> Branches;
for (const auto &Expansion : Expansions) {
auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
// Recursively collect branches from nested expansions.
auto NestedExpansions = ExpansionCoverage.getExpansions();
auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions);
Branches.insert(Branches.end(), NestedExBranches.begin(),
NestedExBranches.end());
// Add branches from this level of expansion.
auto ExBranches = ExpansionCoverage.getBranches();
for (auto B : ExBranches)
if (B.FileID == Expansion.FileID)
Branches.push_back(B);
}
return Branches;
}
json::Object renderExpansion(const coverage::CoverageMapping &Coverage,
const coverage::ExpansionRecord &Expansion) {
std::vector<llvm::coverage::ExpansionRecord> Expansions = {Expansion};
return json::Object(
{{"filenames", json::Array(Expansion.Function.Filenames)},
// Mark the beginning and end of this expansion in the source file.
{"source_region", renderRegion(Expansion.Region)},
// Enumerate the coverage information for the expansion.
{"target_regions", renderRegions(Expansion.Function.CountedRegions)}});
{"target_regions", renderRegions(Expansion.Function.CountedRegions)},
// Enumerate the branch coverage information for the expansion.
{"branches",
renderBranchRegions(collectNestedBranches(Coverage, Expansions))}});
}
json::Object renderSummary(const FileCoverageSummary &Summary) {
@ -123,14 +173,22 @@ json::Object renderSummary(const FileCoverageSummary &Summary) {
{"covered", int64_t(Summary.RegionCoverage.getCovered())},
{"notcovered", int64_t(Summary.RegionCoverage.getNumRegions() -
Summary.RegionCoverage.getCovered())},
{"percent", Summary.RegionCoverage.getPercentCovered()}})}});
{"percent", Summary.RegionCoverage.getPercentCovered()}})},
{"branches",
json::Object(
{{"count", int64_t(Summary.BranchCoverage.getNumBranches())},
{"covered", int64_t(Summary.BranchCoverage.getCovered())},
{"notcovered", int64_t(Summary.BranchCoverage.getNumBranches() -
Summary.BranchCoverage.getCovered())},
{"percent", Summary.BranchCoverage.getPercentCovered()}})}});
}
json::Array renderFileExpansions(const coverage::CoverageData &FileCoverage,
json::Array renderFileExpansions(const coverage::CoverageMapping &Coverage,
const coverage::CoverageData &FileCoverage,
const FileCoverageSummary &FileReport) {
json::Array ExpansionArray;
for (const auto &Expansion : FileCoverage.getExpansions())
ExpansionArray.push_back(renderExpansion(Expansion));
ExpansionArray.push_back(renderExpansion(Coverage, Expansion));
return ExpansionArray;
}
@ -142,6 +200,14 @@ json::Array renderFileSegments(const coverage::CoverageData &FileCoverage,
return SegmentArray;
}
json::Array renderFileBranches(const coverage::CoverageData &FileCoverage,
const FileCoverageSummary &FileReport) {
json::Array BranchArray;
for (const auto &Branch : FileCoverage.getBranches())
BranchArray.push_back(renderBranch(Branch));
return BranchArray;
}
json::Object renderFile(const coverage::CoverageMapping &Coverage,
const std::string &Filename,
const FileCoverageSummary &FileReport,
@ -151,8 +217,10 @@ json::Object renderFile(const coverage::CoverageMapping &Coverage,
// Calculate and render detailed coverage information for given file.
auto FileCoverage = Coverage.getCoverageForFile(Filename);
File["segments"] = renderFileSegments(FileCoverage, FileReport);
File["branches"] = renderFileBranches(FileCoverage, FileReport);
if (!Options.SkipExpansions) {
File["expansions"] = renderFileExpansions(FileCoverage, FileReport);
File["expansions"] =
renderFileExpansions(Coverage, FileCoverage, FileReport);
}
}
File["summary"] = renderSummary(FileReport);
@ -197,6 +265,7 @@ json::Array renderFunctions(
json::Object({{"name", F.Name},
{"count", clamp_uint64_to_int64(F.ExecutionCount)},
{"regions", renderRegions(F.CountedRegions)},
{"branches", renderBranchRegions(F.CountedBranchRegions)},
{"filenames", json::Array(F.Filenames)}}));
return FunctionArray;
}

View File

@ -26,6 +26,10 @@
// - "FNH:<number of functions hit>"
// - for each instrumented line:
// - "DA:<line number>,<execution count>[,<checksum>]
// - for each branch:
// - "BRDA:<line number>,<branch pair id>,<branch id>,<count>"
// - "BRF:<number of branches found>"
// - "BRH:<number of branches hit>"
// - "LH:<number of lines with non-zero execution count>"
// - "LF:<number of instrumented lines>"
// - "end_of_record"
@ -71,11 +75,102 @@ void renderLineExecutionCounts(raw_ostream &OS,
}
}
std::vector<llvm::coverage::CountedRegion>
collectNestedBranches(const coverage::CoverageMapping &Coverage,
ArrayRef<llvm::coverage::ExpansionRecord> Expansions,
int ViewDepth = 0, int SrcLine = 0) {
std::vector<llvm::coverage::CountedRegion> Branches;
for (const auto &Expansion : Expansions) {
auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
// If we're at the top level, set the corresponding source line.
if (ViewDepth == 0)
SrcLine = Expansion.Region.LineStart;
// Recursively collect branches from nested expansions.
auto NestedExpansions = ExpansionCoverage.getExpansions();
auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions,
ViewDepth + 1, SrcLine);
Branches.insert(Branches.end(), NestedExBranches.begin(),
NestedExBranches.end());
// Add branches from this level of expansion.
auto ExBranches = ExpansionCoverage.getBranches();
for (auto B : ExBranches)
if (B.FileID == Expansion.FileID) {
B.LineStart = SrcLine;
Branches.push_back(B);
}
}
return Branches;
}
bool sortLine(llvm::coverage::CountedRegion I,
llvm::coverage::CountedRegion J) {
return (I.LineStart < J.LineStart) ||
((I.LineStart == J.LineStart) && (I.ColumnStart < J.ColumnStart));
}
void renderBranchExecutionCounts(raw_ostream &OS,
const coverage::CoverageMapping &Coverage,
const coverage::CoverageData &FileCoverage) {
std::vector<llvm::coverage::CountedRegion> Branches =
FileCoverage.getBranches();
// Recursively collect branches for all file expansions.
std::vector<llvm::coverage::CountedRegion> ExBranches =
collectNestedBranches(Coverage, FileCoverage.getExpansions());
// Append Expansion Branches to Source Branches.
Branches.insert(Branches.end(), ExBranches.begin(), ExBranches.end());
// Sort branches based on line number to ensure branches corresponding to the
// same source line are counted together.
std::sort(Branches.begin(), Branches.end(), sortLine);
auto NextBranch = Branches.begin();
auto EndBranch = Branches.end();
// Branches with the same source line are enumerated individually
// (BranchIndex) as well as based on True/False pairs (PairIndex).
while (NextBranch != EndBranch) {
unsigned CurrentLine = NextBranch->LineStart;
unsigned PairIndex = 0;
unsigned BranchIndex = 0;
while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) {
if (!NextBranch->Folded) {
unsigned BC1 = NextBranch->ExecutionCount;
unsigned BC2 = NextBranch->FalseExecutionCount;
bool BranchNotExecuted = (BC1 == 0 && BC2 == 0);
for (int I = 0; I < 2; I++, BranchIndex++) {
OS << "BRDA:" << CurrentLine << ',' << PairIndex << ','
<< BranchIndex;
if (BranchNotExecuted)
OS << ',' << '-' << '\n';
else
OS << ',' << (I == 0 ? BC1 : BC2) << '\n';
}
PairIndex++;
}
NextBranch++;
}
}
}
void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n'
<< "LH:" << Summary.LineCoverage.getCovered() << '\n';
}
void renderBranchSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
OS << "BRF:" << Summary.BranchCoverage.getNumBranches() << '\n'
<< "BFH:" << Summary.BranchCoverage.getCovered() << '\n';
}
void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
const std::string &Filename,
const FileCoverageSummary &FileReport, bool ExportSummaryOnly,
@ -91,7 +186,9 @@ void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
// Calculate and render detailed coverage information for given file.
auto FileCoverage = Coverage.getCoverageForFile(Filename);
renderLineExecutionCounts(OS, FileCoverage);
renderBranchExecutionCounts(OS, Coverage, FileCoverage);
}
renderBranchSummary(OS, FileReport);
renderLineSummary(OS, FileReport);
OS << "end_of_record\n";

View File

@ -86,9 +86,9 @@ Column column(StringRef Str, unsigned Width, const T &Value) {
}
// Specify the default column widths.
size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10,
16, 16, 10, 12, 18, 10};
size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8};
size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10, 16,
16, 10, 12, 18, 10, 12, 18, 10};
size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8, 10, 8, 8};
/// Adjust column widths to fit long file paths and function names.
void adjustColumnWidths(ArrayRef<StringRef> Files,
@ -239,6 +239,23 @@ void CoverageReport::render(const FileCoverageSummary &File,
<< '%';
else
OS << column("-", FileReportColumns[12], Column::RightAlignment);
if (Options.ShowBranchSummary) {
OS << format("%*u", FileReportColumns[13],
(unsigned)File.BranchCoverage.getNumBranches());
Options.colored_ostream(OS, LineCoverageColor)
<< format("%*u", FileReportColumns[14],
(unsigned)(File.BranchCoverage.getNumBranches() -
File.BranchCoverage.getCovered()));
if (File.BranchCoverage.getNumBranches())
Options.colored_ostream(OS, LineCoverageColor)
<< format("%*.2f", FileReportColumns[15] - 1,
File.BranchCoverage.getPercentCovered())
<< '%';
else
OS << column("-", FileReportColumns[15], Column::RightAlignment);
}
OS << "\n";
}
@ -273,6 +290,19 @@ void CoverageReport::render(const FunctionCoverageSummary &Function,
<< format("%*.2f", FunctionReportColumns[6] - 1,
Function.LineCoverage.getPercentCovered())
<< '%';
if (Options.ShowBranchSummary) {
OS << format("%*u", FunctionReportColumns[7],
(unsigned)Function.BranchCoverage.getNumBranches());
Options.colored_ostream(OS, LineCoverageColor)
<< format("%*u", FunctionReportColumns[8],
(unsigned)(Function.BranchCoverage.getNumBranches() -
Function.BranchCoverage.getCovered()));
Options.colored_ostream(
OS, determineCoveragePercentageColor(Function.BranchCoverage))
<< format("%*.2f", FunctionReportColumns[9] - 1,
Function.BranchCoverage.getPercentCovered())
<< '%';
}
OS << "\n";
}
@ -301,6 +331,10 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
<< column("Lines", FunctionReportColumns[4], Column::RightAlignment)
<< column("Miss", FunctionReportColumns[5], Column::RightAlignment)
<< column("Cover", FunctionReportColumns[6], Column::RightAlignment);
if (Options.ShowBranchSummary)
OS << column("Branches", FunctionReportColumns[7], Column::RightAlignment)
<< column("Miss", FunctionReportColumns[8], Column::RightAlignment)
<< column("Cover", FunctionReportColumns[9], Column::RightAlignment);
OS << "\n";
renderDivider(FunctionReportColumns, OS);
OS << "\n";
@ -310,6 +344,7 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
++Totals.ExecutionCount;
Totals.RegionCoverage += Function.RegionCoverage;
Totals.LineCoverage += Function.LineCoverage;
Totals.BranchCoverage += Function.BranchCoverage;
render(Function, DC, OS);
}
if (Totals.ExecutionCount) {
@ -420,7 +455,13 @@ void CoverageReport::renderFileReports(
<< column("Executed", FileReportColumns[9], Column::RightAlignment);
OS << column("Lines", FileReportColumns[10], Column::RightAlignment)
<< column("Missed Lines", FileReportColumns[11], Column::RightAlignment)
<< column("Cover", FileReportColumns[12], Column::RightAlignment) << "\n";
<< column("Cover", FileReportColumns[12], Column::RightAlignment);
if (Options.ShowBranchSummary)
OS << column("Branches", FileReportColumns[13], Column::RightAlignment)
<< column("Missed Branches", FileReportColumns[14],
Column::RightAlignment)
<< column("Cover", FileReportColumns[15], Column::RightAlignment);
OS << "\n";
renderDivider(FileReportColumns, OS);
OS << "\n";

View File

@ -16,6 +16,34 @@
using namespace llvm;
using namespace coverage;
static void sumBranches(size_t &NumBranches, size_t &CoveredBranches,
const ArrayRef<CountedRegion> &Branches) {
for (const auto &BR : Branches) {
// Skip folded branches.
if (BR.Folded)
continue;
// "True" Condition Branches.
++NumBranches;
if (BR.ExecutionCount > 0)
++CoveredBranches;
// "False" Condition Branches.
++NumBranches;
if (BR.FalseExecutionCount > 0)
++CoveredBranches;
}
}
static void sumBranchExpansions(size_t &NumBranches, size_t &CoveredBranches,
const CoverageMapping &CM,
ArrayRef<ExpansionRecord> Expansions) {
for (const auto &Expansion : Expansions) {
auto CE = CM.getCoverageForExpansion(Expansion);
sumBranches(NumBranches, CoveredBranches, CE.getBranches());
sumBranchExpansions(NumBranches, CoveredBranches, CM, CE.getExpansions());
}
}
FunctionCoverageSummary
FunctionCoverageSummary::get(const CoverageMapping &CM,
const coverage::FunctionRecord &Function) {
@ -40,10 +68,16 @@ FunctionCoverageSummary::get(const CoverageMapping &CM,
++CoveredLines;
}
// Compute the branch coverage, including branches from expansions.
size_t NumBranches = 0, CoveredBranches = 0;
sumBranches(NumBranches, CoveredBranches, CD.getBranches());
sumBranchExpansions(NumBranches, CoveredBranches, CM, CD.getExpansions());
return FunctionCoverageSummary(
Function.Name, Function.ExecutionCount,
RegionCoverageInfo(CoveredRegions, NumCodeRegions),
LineCoverageInfo(CoveredLines, NumLines));
LineCoverageInfo(CoveredLines, NumLines),
BranchCoverageInfo(CoveredBranches, NumBranches));
}
FunctionCoverageSummary
@ -62,9 +96,15 @@ FunctionCoverageSummary::get(const InstantiationGroup &Group,
Summary.ExecutionCount = Group.getTotalExecutionCount();
Summary.RegionCoverage = Summaries[0].RegionCoverage;
Summary.LineCoverage = Summaries[0].LineCoverage;
Summary.BranchCoverage = Summaries[0].BranchCoverage;
for (const auto &FCS : Summaries.drop_front()) {
Summary.RegionCoverage.merge(FCS.RegionCoverage);
Summary.LineCoverage.merge(FCS.LineCoverage);
// Sum branch coverage across instantiation groups for the summary rather
// than "merge" the maximum count. This is a clearer view into whether all
// created branches are covered.
Summary.BranchCoverage += FCS.BranchCoverage;
}
return Summary;
}

View File

@ -101,6 +101,42 @@ public:
}
};
/// Provides information about branches coverage for a function/file.
class BranchCoverageInfo {
/// The number of branches that were executed at least once.
size_t Covered;
/// The total number of branches in a function/file.
size_t NumBranches;
public:
BranchCoverageInfo() : Covered(0), NumBranches(0) {}
BranchCoverageInfo(size_t Covered, size_t NumBranches)
: Covered(Covered), NumBranches(NumBranches) {
assert(Covered <= NumBranches && "Covered branches over-counted");
}
BranchCoverageInfo &operator+=(const BranchCoverageInfo &RHS) {
Covered += RHS.Covered;
NumBranches += RHS.NumBranches;
return *this;
}
size_t getCovered() const { return Covered; }
size_t getNumBranches() const { return NumBranches; }
bool isFullyCovered() const { return Covered == NumBranches; }
double getPercentCovered() const {
assert(Covered <= NumBranches && "Covered branches over-counted");
if (NumBranches == 0)
return 0.0;
return double(Covered) / double(NumBranches) * 100.0;
}
};
/// Provides information about function coverage for a file.
class FunctionCoverageInfo {
/// The number of functions that were executed.
@ -147,15 +183,19 @@ struct FunctionCoverageSummary {
uint64_t ExecutionCount;
RegionCoverageInfo RegionCoverage;
LineCoverageInfo LineCoverage;
BranchCoverageInfo BranchCoverage;
FunctionCoverageSummary(const std::string &Name)
: Name(Name), ExecutionCount(0), RegionCoverage(), LineCoverage() {}
: Name(Name), ExecutionCount(0), RegionCoverage(), LineCoverage(),
BranchCoverage() {}
FunctionCoverageSummary(const std::string &Name, uint64_t ExecutionCount,
const RegionCoverageInfo &RegionCoverage,
const LineCoverageInfo &LineCoverage)
const LineCoverageInfo &LineCoverage,
const BranchCoverageInfo &BranchCoverage)
: Name(Name), ExecutionCount(ExecutionCount),
RegionCoverage(RegionCoverage), LineCoverage(LineCoverage) {}
RegionCoverage(RegionCoverage), LineCoverage(LineCoverage),
BranchCoverage(BranchCoverage) {}
/// Compute the code coverage summary for the given function coverage
/// mapping record.
@ -174,6 +214,7 @@ struct FileCoverageSummary {
StringRef Name;
RegionCoverageInfo RegionCoverage;
LineCoverageInfo LineCoverage;
BranchCoverageInfo BranchCoverage;
FunctionCoverageInfo FunctionCoverage;
FunctionCoverageInfo InstantiationCoverage;
@ -185,6 +226,7 @@ struct FileCoverageSummary {
RegionCoverage += RHS.RegionCoverage;
LineCoverage += RHS.LineCoverage;
FunctionCoverage += RHS.FunctionCoverage;
BranchCoverage += RHS.BranchCoverage;
InstantiationCoverage += RHS.InstantiationCoverage;
return *this;
}
@ -192,6 +234,7 @@ struct FileCoverageSummary {
void addFunction(const FunctionCoverageSummary &Function) {
RegionCoverage += Function.RegionCoverage;
LineCoverage += Function.LineCoverage;
BranchCoverage += Function.BranchCoverage;
FunctionCoverage.addFunction(/*Covered=*/Function.ExecutionCount > 0);
}

View File

@ -23,20 +23,26 @@ struct CoverageViewOptions {
Lcov
};
enum class BranchOutputType { Count, Percent, Off };
bool Debug;
bool Colors;
bool ShowLineNumbers;
bool ShowLineStats;
bool ShowRegionMarkers;
bool ShowBranchCounts;
bool ShowBranchPercents;
bool ShowExpandedRegions;
bool ShowFunctionInstantiations;
bool ShowFullFilenames;
bool ShowBranchSummary;
bool ShowRegionSummary;
bool ShowInstantiationSummary;
bool ExportSummaryOnly;
bool SkipExpansions;
bool SkipFunctions;
OutputFormat Format;
BranchOutputType ShowBranches;
std::string ShowOutputDirectory;
std::vector<std::string> DemanglerOpts;
uint32_t TabSize;

View File

@ -132,7 +132,8 @@ bool SourceCoverageView::shouldRenderRegionMarkers(
}
bool SourceCoverageView::hasSubViews() const {
return !ExpansionSubViews.empty() || !InstantiationSubViews.empty();
return !ExpansionSubViews.empty() || !InstantiationSubViews.empty() ||
!BranchSubViews.empty();
}
std::unique_ptr<SourceCoverageView>
@ -167,6 +168,12 @@ void SourceCoverageView::addExpansion(
ExpansionSubViews.emplace_back(Region, std::move(View));
}
void SourceCoverageView::addBranch(unsigned Line,
ArrayRef<CountedRegion> Regions,
std::unique_ptr<SourceCoverageView> View) {
BranchSubViews.emplace_back(Line, Regions, std::move(View));
}
void SourceCoverageView::addInstantiation(
StringRef FunctionName, unsigned Line,
std::unique_ptr<SourceCoverageView> View) {
@ -187,14 +194,17 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
renderTableHeader(OS, (ViewDepth > 0) ? 0 : getFirstUncoveredLineNo(),
ViewDepth);
// We need the expansions and instantiations sorted so we can go through them
// while we iterate lines.
// We need the expansions, instantiations, and branches sorted so we can go
// through them while we iterate lines.
llvm::stable_sort(ExpansionSubViews);
llvm::stable_sort(InstantiationSubViews);
llvm::stable_sort(BranchSubViews);
auto NextESV = ExpansionSubViews.begin();
auto EndESV = ExpansionSubViews.end();
auto NextISV = InstantiationSubViews.begin();
auto EndISV = InstantiationSubViews.end();
auto NextBRV = BranchSubViews.begin();
auto EndBRV = BranchSubViews.end();
// Get the coverage information for the file.
auto StartSegment = CoverageInfo.begin();
@ -234,7 +244,7 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
if (shouldRenderRegionMarkers(*LCI))
renderRegionMarkers(OS, *LCI, ViewDepth);
// Show the expansions and instantiations for this line.
// Show the expansions, instantiations, and branches for this line.
bool RenderedSubView = false;
for (; NextESV != EndESV && NextESV->getLine() == LI.line_number();
++NextESV) {
@ -257,6 +267,11 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
renderInstantiationView(OS, *NextISV, ViewDepth + 1);
RenderedSubView = true;
}
for (; NextBRV != EndBRV && NextBRV->Line == LI.line_number(); ++NextBRV) {
renderViewDivider(OS, ViewDepth + 1);
renderBranchView(OS, *NextBRV, ViewDepth + 1);
RenderedSubView = true;
}
if (RenderedSubView)
renderViewDivider(OS, ViewDepth + 1);
renderLineSuffix(OS, ViewDepth);

View File

@ -67,6 +67,23 @@ struct InstantiationView {
}
};
/// A view that represents one or more branch regions on a given source line.
struct BranchView {
std::vector<CountedRegion> Regions;
std::unique_ptr<SourceCoverageView> View;
unsigned Line;
BranchView(unsigned Line, ArrayRef<CountedRegion> Regions,
std::unique_ptr<SourceCoverageView> View)
: Regions(Regions), View(std::move(View)), Line(Line) {}
unsigned getLine() const { return Line; }
friend bool operator<(const BranchView &LHS, const BranchView &RHS) {
return LHS.Line < RHS.Line;
}
};
/// A file manager that handles format-aware file creation.
class CoveragePrinter {
public:
@ -140,6 +157,9 @@ class SourceCoverageView {
/// A container for all expansions (e.g macros) in the source on display.
std::vector<ExpansionView> ExpansionSubViews;
/// A container for all branches in the source on display.
std::vector<BranchView> BranchSubViews;
/// A container for all instantiations (e.g template functions) in the source
/// on display.
std::vector<InstantiationView> InstantiationSubViews;
@ -209,6 +229,10 @@ protected:
virtual void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
unsigned ViewDepth) = 0;
/// Render a branch view and any nested views.
virtual void renderBranchView(raw_ostream &OS, BranchView &BRV,
unsigned ViewDepth) = 0;
/// Render \p Title, a project title if one is available, and the
/// created time.
virtual void renderTitle(raw_ostream &OS, StringRef CellText) = 0;
@ -255,6 +279,10 @@ public:
void addInstantiation(StringRef FunctionName, unsigned Line,
std::unique_ptr<SourceCoverageView> View);
/// Add a branch subview to this view.
void addBranch(unsigned Line, ArrayRef<CountedRegion> Regions,
std::unique_ptr<SourceCoverageView> View);
/// Print the code coverage information for a specific portion of a
/// source file to the output stream.
void print(raw_ostream &OS, bool WholeFile, bool ShowSourceName,

View File

@ -313,6 +313,8 @@ static void emitColumnLabelsForIndex(raw_ostream &OS,
Columns.emplace_back(tag("td", "Line Coverage", "column-entry-bold"));
if (Opts.ShowRegionSummary)
Columns.emplace_back(tag("td", "Region Coverage", "column-entry-bold"));
if (Opts.ShowBranchSummary)
Columns.emplace_back(tag("td", "Branch Coverage", "column-entry-bold"));
OS << tag("tr", join(Columns.begin(), Columns.end(), ""));
}
@ -378,6 +380,10 @@ void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF,
AddCoverageTripleToColumn(FCS.RegionCoverage.getCovered(),
FCS.RegionCoverage.getNumRegions(),
FCS.RegionCoverage.getPercentCovered());
if (Opts.ShowBranchSummary)
AddCoverageTripleToColumn(FCS.BranchCoverage.getCovered(),
FCS.BranchCoverage.getNumBranches(),
FCS.BranchCoverage.getPercentCovered());
if (IsTotals)
OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row-bold");
@ -650,6 +656,72 @@ void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS,
OS << EndExpansionDiv;
}
void SourceCoverageViewHTML::renderBranchView(raw_ostream &OS, BranchView &BRV,
unsigned ViewDepth) {
// Render the child subview.
if (getOptions().Debug)
errs() << "Branch at line " << BRV.getLine() << '\n';
OS << BeginExpansionDiv;
OS << BeginPre;
for (const auto &R : BRV.Regions) {
// Calculate TruePercent and False Percent.
double TruePercent = 0.0;
double FalsePercent = 0.0;
unsigned Total = R.ExecutionCount + R.FalseExecutionCount;
if (!getOptions().ShowBranchCounts && Total != 0) {
TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0;
FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0;
}
// Display Line + Column.
std::string LineNoStr = utostr(uint64_t(R.LineStart));
std::string ColNoStr = utostr(uint64_t(R.ColumnStart));
std::string TargetName = "L" + LineNoStr;
OS << " Branch (";
OS << tag("span",
a("#" + TargetName, tag("span", LineNoStr + ":" + ColNoStr),
TargetName),
"line-number") +
"): [";
if (R.Folded) {
OS << "Folded - Ignored]\n";
continue;
}
// Display TrueCount or TruePercent.
std::string TrueColor = R.ExecutionCount ? "None" : "red";
std::string TrueCovClass =
(R.ExecutionCount > 0) ? "covered-line" : "uncovered-line";
OS << tag("span", "True", TrueColor);
OS << ": ";
if (getOptions().ShowBranchCounts)
OS << tag("span", formatCount(R.ExecutionCount), TrueCovClass) << ", ";
else
OS << format("%0.2f", TruePercent) << "%, ";
// Display FalseCount or FalsePercent.
std::string FalseColor = R.FalseExecutionCount ? "None" : "red";
std::string FalseCovClass =
(R.FalseExecutionCount > 0) ? "covered-line" : "uncovered-line";
OS << tag("span", "False", FalseColor);
OS << ": ";
if (getOptions().ShowBranchCounts)
OS << tag("span", formatCount(R.FalseExecutionCount), FalseCovClass);
else
OS << format("%0.2f", FalsePercent) << "%";
OS << "]\n";
}
OS << EndPre;
OS << EndExpansionDiv;
}
void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS,
InstantiationView &ISV,
unsigned ViewDepth) {

View File

@ -68,6 +68,9 @@ class SourceCoverageViewHTML : public SourceCoverageView {
void renderExpansionView(raw_ostream &OS, ExpansionView &ESV,
unsigned ViewDepth) override;
void renderBranchView(raw_ostream &OS, BranchView &BRV,
unsigned ViewDepth) override;
void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
unsigned ViewDepth) override;

View File

@ -10,11 +10,12 @@
///
//===----------------------------------------------------------------------===//
#include "CoverageReport.h"
#include "SourceCoverageViewText.h"
#include "CoverageReport.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Format.h"
using namespace llvm;
@ -222,6 +223,53 @@ void SourceCoverageViewText::renderExpansionView(raw_ostream &OS,
/*ShowTitle=*/false, ViewDepth + 1);
}
void SourceCoverageViewText::renderBranchView(raw_ostream &OS, BranchView &BRV,
unsigned ViewDepth) {
// Render the child subview.
if (getOptions().Debug)
errs() << "Branch at line " << BRV.getLine() << '\n';
for (const auto &R : BRV.Regions) {
double TruePercent = 0.0;
double FalsePercent = 0.0;
unsigned Total = R.ExecutionCount + R.FalseExecutionCount;
if (!getOptions().ShowBranchCounts && Total != 0) {
TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0;
FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0;
}
renderLinePrefix(OS, ViewDepth);
OS << " Branch (" << R.LineStart << ":" << R.ColumnStart << "): [";
if (R.Folded) {
OS << "Folded - Ignored]\n";
continue;
}
colored_ostream(OS, raw_ostream::RED,
getOptions().Colors && !R.ExecutionCount,
/*Bold=*/false, /*BG=*/true)
<< "True";
if (getOptions().ShowBranchCounts)
OS << ": " << formatCount(R.ExecutionCount) << ", ";
else
OS << ": " << format("%0.2f", TruePercent) << "%, ";
colored_ostream(OS, raw_ostream::RED,
getOptions().Colors && !R.FalseExecutionCount,
/*Bold=*/false, /*BG=*/true)
<< "False";
if (getOptions().ShowBranchCounts)
OS << ": " << formatCount(R.FalseExecutionCount);
else
OS << ": " << format("%0.2f", FalsePercent) << "%";
OS << "]\n";
}
}
void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,
InstantiationView &ISV,
unsigned ViewDepth) {

View File

@ -59,6 +59,9 @@ class SourceCoverageViewText : public SourceCoverageView {
void renderExpansionView(raw_ostream &OS, ExpansionView &ESV,
unsigned ViewDepth) override;
void renderBranchView(raw_ostream &OS, BranchView &BRV,
unsigned ViewDepth) override;
void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
unsigned ViewDepth) override;