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 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 .. option:: -show-line-counts
Show the execution counts for each line. Defaults to true, unless another 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 universal binary or to use an architecture that does not match a
non-universal binary. non-universal binary.
.. option:: -show-branch-summary
Show statistics for all branch conditions. Defaults to true.
.. option:: -show-functions .. option:: -show-functions
Show coverage summaries for each function. Defaults to false. 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 *BIN*,... using the profile data *PROFILE* in either JSON or lcov trace file
format. format.
When exporting JSON, the regions, functions, expansions, and summaries of the When exporting JSON, the regions, functions, branches, expansions, and
coverage data will be exported. When exporting an lcov trace file, the summaries of the coverage data will be exported. When exporting an lcov trace
line-based coverage and summaries will be exported. file, the line-based coverage, branch coverage, and summaries will be exported.
The exported data can optionally be filtered to only export the coverage The exported data can optionally be filtered to only export the coverage
for the files listed in *SOURCES*. 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'> 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> <span style='background-color:#4A789C'>}</span>
</pre>` </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: .. _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 Without them, the tool would think that those lines and regions were still
executed, as it doesn't possess the frontend's knowledge. 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 LLVM IR Representation
====================== ======================
@ -242,7 +266,14 @@ too deeply).
[32 x i8] c"..." ; Encoded data (dissected later) [32 x i8] c"..." ; Encoded data (dissected later)
}, section "__llvm_covmap", align 8 }, 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 * Function records are now named symbols, and are marked *linkonce_odr*. This
allows linkers to merge duplicate function records. Merging of duplicate allows linkers to merge duplicate function records. Merging of duplicate
@ -511,7 +542,8 @@ or
``[pseudo-counter]`` ``[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 The value of the counter's tag distinguishes between the counters and
pseudo-counters --- if the tag is zero, than this header contains a 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. * 0 - This mapping region is a code region with a counter of zero.
* 2 - This mapping region is a skipped region. * 2 - This mapping region is a skipped region.
* 4 - This mapping region is a branch region.
.. _source range: .. _source range:

View File

@ -90,6 +90,8 @@ private:
/// A Counter is an abstract value that describes how to compute the /// 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. /// execution count for a region of code using the collected profile count data.
struct Counter { 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 }; enum CounterKind { Zero, CounterValueReference, Expression };
static const unsigned EncodingTagBits = 2; static const unsigned EncodingTagBits = 2;
static const unsigned EncodingTagMask = 0x3; 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 /// 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. /// 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; Counter Count;
/// Secondary Counter used for Branch Regions (FalseCount).
Counter FalseCount;
unsigned FileID, ExpandedFileID; unsigned FileID, ExpandedFileID;
unsigned LineStart, ColumnStart, LineEnd, ColumnEnd; unsigned LineStart, ColumnStart, LineEnd, ColumnEnd;
RegionKind Kind; RegionKind Kind;
@ -234,6 +246,15 @@ struct CounterMappingRegion {
LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd), LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd),
ColumnEnd(ColumnEnd), Kind(Kind) {} 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 static CounterMappingRegion
makeRegion(Counter Count, unsigned FileID, unsigned LineStart, makeRegion(Counter Count, unsigned FileID, unsigned LineStart,
unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) { unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) {
@ -263,6 +284,14 @@ struct CounterMappingRegion {
LineEnd, (1U << 31) | ColumnEnd, GapRegion); 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 { inline LineColPair startLoc() const {
return LineColPair(LineStart, ColumnStart); return LineColPair(LineStart, ColumnStart);
} }
@ -273,9 +302,17 @@ struct CounterMappingRegion {
/// Associates a source range with an execution count. /// Associates a source range with an execution count.
struct CountedRegion : public CounterMappingRegion { struct CountedRegion : public CounterMappingRegion {
uint64_t ExecutionCount; uint64_t ExecutionCount;
uint64_t FalseExecutionCount;
bool Folded;
CountedRegion(const CounterMappingRegion &R, uint64_t ExecutionCount) 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 /// A Counter mapping context is used to connect the counters, expressions
@ -312,6 +349,8 @@ struct FunctionRecord {
std::vector<std::string> Filenames; std::vector<std::string> Filenames;
/// Regions in the function along with their counts. /// Regions in the function along with their counts.
std::vector<CountedRegion> CountedRegions; 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. /// The number of times this function was executed.
uint64_t ExecutionCount = 0; uint64_t ExecutionCount = 0;
@ -321,10 +360,19 @@ struct FunctionRecord {
FunctionRecord(FunctionRecord &&FR) = default; FunctionRecord(FunctionRecord &&FR) = default;
FunctionRecord &operator=(FunctionRecord &&) = 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()) if (CountedRegions.empty())
ExecutionCount = Count; ExecutionCount = Count;
CountedRegions.emplace_back(Region, Count); CountedRegions.emplace_back(Region, Count, FalseCount);
} }
}; };
@ -403,7 +451,8 @@ struct CoverageSegment {
IsRegionEntry(IsRegionEntry), IsGapRegion(false) {} IsRegionEntry(IsRegionEntry), IsGapRegion(false) {}
CoverageSegment(unsigned Line, unsigned Col, uint64_t Count, 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), : Line(Line), Col(Col), Count(Count), HasCount(true),
IsRegionEntry(IsRegionEntry), IsGapRegion(IsGapRegion) {} IsRegionEntry(IsRegionEntry), IsGapRegion(IsGapRegion) {}
@ -483,6 +532,7 @@ class CoverageData {
std::string Filename; std::string Filename;
std::vector<CoverageSegment> Segments; std::vector<CoverageSegment> Segments;
std::vector<ExpansionRecord> Expansions; std::vector<ExpansionRecord> Expansions;
std::vector<CountedRegion> BranchRegions;
public: public:
CoverageData() = default; CoverageData() = default;
@ -506,6 +556,9 @@ public:
/// Expansions that can be further processed. /// Expansions that can be further processed.
ArrayRef<ExpansionRecord> getExpansions() const { return Expansions; } 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. /// The mapping of profile information to coverage data.
@ -941,7 +994,9 @@ enum CovMapVersion {
Version3 = 2, Version3 = 2,
// Function records are named, uniqued, and moved to a dedicated section. // Function records are named, uniqued, and moved to a dedicated section.
Version4 = 3, 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 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 // In this version, the frontend PGO stable hash algorithm got fixed and
// may produce hashes different from Version5. // may produce hashes different from Version5.
Version6 = 6, 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 CurrentVersion = INSTR_PROF_INDEX_VERSION
}; };
const uint64_t Version = ProfVersion::CurrentVersion; const uint64_t Version = ProfVersion::CurrentVersion;

View File

@ -647,9 +647,9 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
/* Raw profile format version (start from 1). */ /* Raw profile format version (start from 1). */
#define INSTR_PROF_RAW_VERSION 5 #define INSTR_PROF_RAW_VERSION 5
/* Indexed profile format version (start from 1). */ /* 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). */ /* 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 /* 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 * 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)); consumeError(std::move(E));
return Error::success(); 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. // 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)) if (MainFileID && isExpansion(CR, *MainFileID))
FileCoverage.Expansions.emplace_back(CR, Function); 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"); LLVM_DEBUG(dbgs() << "Emitting segments for file: " << Filename << "\n");
@ -719,6 +728,10 @@ CoverageMapping::getCoverageForFunction(const FunctionRecord &Function) const {
if (isExpansion(CR, *MainFileID)) if (isExpansion(CR, *MainFileID))
FunctionCoverage.Expansions.emplace_back(CR, Function); 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 LLVM_DEBUG(dbgs() << "Emitting segments for function: " << Function.Name
<< "\n"); << "\n");
@ -738,6 +751,10 @@ CoverageData CoverageMapping::getCoverageForExpansion(
if (isExpansion(CR, Expansion.FileID)) if (isExpansion(CR, Expansion.FileID))
ExpansionCoverage.Expansions.emplace_back(CR, Expansion.Function); 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 " LLVM_DEBUG(dbgs() << "Emitting segments for expansion of file "
<< Expansion.FileID << "\n"); << Expansion.FileID << "\n");

View File

@ -213,7 +213,7 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
return Err; return Err;
unsigned LineStart = 0; unsigned LineStart = 0;
for (size_t I = 0; I < NumRegions; ++I) { for (size_t I = 0; I < NumRegions; ++I) {
Counter C; Counter C, C2;
CounterMappingRegion::RegionKind Kind = CounterMappingRegion::CodeRegion; CounterMappingRegion::RegionKind Kind = CounterMappingRegion::CodeRegion;
// Read the combined counter + region kind. // Read the combined counter + region kind.
@ -223,6 +223,18 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
return Err; return Err;
unsigned Tag = EncodedCounterAndRegion & Counter::EncodingTagMask; unsigned Tag = EncodedCounterAndRegion & Counter::EncodingTagMask;
uint64_t ExpandedFileID = 0; 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 (Tag != Counter::Zero) {
if (auto Err = decodeCounter(EncodedCounterAndRegion, C)) if (auto Err = decodeCounter(EncodedCounterAndRegion, C))
return Err; return Err;
@ -243,6 +255,14 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
case CounterMappingRegion::SkippedRegion: case CounterMappingRegion::SkippedRegion:
Kind = CounterMappingRegion::SkippedRegion; Kind = CounterMappingRegion::SkippedRegion;
break; 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: default:
return make_error<CoverageMapError>(coveragemap_error::malformed); return make_error<CoverageMapError>(coveragemap_error::malformed);
} }
@ -294,7 +314,7 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
dbgs() << "\n"; dbgs() << "\n";
}); });
auto CMR = CounterMappingRegion(C, InferredFileID, ExpandedFileID, auto CMR = CounterMappingRegion(C, C2, InferredFileID, ExpandedFileID,
LineStart, ColumnStart, LineStart, ColumnStart,
LineStart + NumLines, ColumnEnd, Kind); LineStart + NumLines, ColumnEnd, Kind);
if (CMR.startLoc() > CMR.endLoc()) if (CMR.startLoc() > CMR.endLoc())
@ -600,7 +620,7 @@ public:
CovBuf += FilenamesSize; CovBuf += FilenamesSize;
FilenameRange FileRange(FilenamesBegin, Filenames.size() - FilenamesBegin); 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 // Map a hash of the filenames region to the filename range associated
// with this coverage header. // with this coverage header.
int64_t FilenamesRef = int64_t FilenamesRef =
@ -628,7 +648,7 @@ public:
// This is a no-op in Version4 (coverage mappings are not affixed to the // This is a no-op in Version4 (coverage mappings are not affixed to the
// coverage header). // coverage header).
const char *MappingBuf = CovBuf; const char *MappingBuf = CovBuf;
if (Version == CovMapVersion::Version4 && CoverageSize != 0) if (Version >= CovMapVersion::Version4 && CoverageSize != 0)
return make_error<CoverageMapError>(coveragemap_error::malformed); return make_error<CoverageMapError>(coveragemap_error::malformed);
CovBuf += CoverageSize; CovBuf += CoverageSize;
const char *MappingEnd = CovBuf; const char *MappingEnd = CovBuf;
@ -682,7 +702,7 @@ public:
if (FileRange && !FileRange->isInvalid()) { if (FileRange && !FileRange->isInvalid()) {
StringRef Mapping = StringRef Mapping =
CFR->template getCoverageMapping<Endian>(OutOfLineMappingBuf); CFR->template getCoverageMapping<Endian>(OutOfLineMappingBuf);
if (Version == CovMapVersion::Version4 && if (Version >= CovMapVersion::Version4 &&
Mapping.data() + Mapping.size() > FuncRecBufEnd) Mapping.data() + Mapping.size() > FuncRecBufEnd)
return make_error<CoverageMapError>(coveragemap_error::malformed); return make_error<CoverageMapError>(coveragemap_error::malformed);
if (Error Err = insertFunctionRecordIfNeeded(CFR, Mapping, *FileRange)) if (Error Err = insertFunctionRecordIfNeeded(CFR, Mapping, *FileRange))
@ -711,6 +731,7 @@ Expected<std::unique_ptr<CovMapFuncRecordReader>> CovMapFuncRecordReader::get(
case CovMapVersion::Version2: case CovMapVersion::Version2:
case CovMapVersion::Version3: case CovMapVersion::Version3:
case CovMapVersion::Version4: case CovMapVersion::Version4:
case CovMapVersion::Version5:
// Decompress the name data. // Decompress the name data.
if (Error E = P.create(P.getNameData())) if (Error E = P.create(P.getNameData()))
return std::move(E); return std::move(E);
@ -723,6 +744,9 @@ Expected<std::unique_ptr<CovMapFuncRecordReader>> CovMapFuncRecordReader::get(
else if (Version == CovMapVersion::Version4) else if (Version == CovMapVersion::Version4)
return std::make_unique<VersionedCovMapFuncRecordReader< return std::make_unique<VersionedCovMapFuncRecordReader<
CovMapVersion::Version4, IntPtrT, Endian>>(P, R, F); 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"); llvm_unreachable("Unsupported version");
} }
@ -766,7 +790,7 @@ static Error readCoverageMappingData(
} }
// In Version4, function records are not affixed to coverage headers. Read // In Version4, function records are not affixed to coverage headers. Read
// the records from their dedicated section. // the records from their dedicated section.
if (Version == CovMapVersion::Version4) if (Version >= CovMapVersion::Version4)
return Reader->readFunctionRecords(FuncRecBuf, FuncRecBufEnd, None, nullptr, return Reader->readFunctionRecords(FuncRecBuf, FuncRecBufEnd, None, nullptr,
nullptr); nullptr);
return Error::success(); return Error::success();

View File

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

View File

@ -1,24 +1,28 @@
CHECK: {"data": CHECK: {"data":
CHECK-SAME: [{ CHECK-SAME: [{
CHECK-SAME: "files":[ CHECK-SAME: "files":[
CHECK-SAME: {"expansions":[], CHECK-SAME: {"branches":[],
CHECK-SAME: "expansions":[],
CHECK-SAME: "filename":"/tmp/binary-formats.c", CHECK-SAME: "filename":"/tmp/binary-formats.c",
CHECK-SAME: "segments": CHECK-SAME: "segments":
CHECK-SAME: 4,40,100,true,true,false CHECK-SAME: 4,40,100,true,true,false
CHECK-SAME: 4,42,0,false,false,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: "instantiations":{"count":1,"covered":1,"percent":100},
CHECK-SAME: "lines":{"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: "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}}}
CHECK-SAME: ], CHECK-SAME: ],
CHECK-SAME: "functions":[ 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: "regions":
CHECK-SAME: 4,40,4,42,100,0,0,0 CHECK-SAME: 4,40,4,42,100,0,0,0
CHECK-SAME: } CHECK-SAME: }
CHECK-SAME: ], CHECK-SAME: ],
CHECK-SAME: "totals": 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: "instantiations":{"count":1,"covered":1,"percent":100},
CHECK-SAME: "lines":{"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: "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: "filenames":["{{[^"]+}}showExpansions.cpp",
CHECK: "source_region":[24,5,24,17,100,0,1,1], CHECK: "source_region":[24,5,24,17,100,0,1,1],
CHECK: "filename":"{{[^"]+}}showExpansions.cpp", 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. # Ignore all header files.
RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \ RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \
RUN: -path-equivalence=/tmp,%S/Inputs -ignore-filename-regex='.*\.h$' \ 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 RUN: | FileCheck -check-prefix=REPORT_IGNORE_HEADERS %s
REPORT_IGNORE_HEADERS-NOT: {{.*}}dec.h{{.*}} REPORT_IGNORE_HEADERS-NOT: {{.*}}dec.h{{.*}}
@ -15,7 +15,7 @@ REPORT_IGNORE_HEADERS: {{^}}TOTAL 1{{.*}}100.00%{{$}}
# Ignore all files from "extra" directory. # Ignore all files from "extra" directory.
RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \ RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \
RUN: -path-equivalence=/tmp,%S/Inputs -ignore-filename-regex='.*extra[/\\].*' \ 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 RUN: | FileCheck -check-prefix=REPORT_IGNORE_DIR %s
REPORT_IGNORE_DIR-NOT: {{.*}}extra{{[/\\]}}dec.h{{.*}} 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. # Ignore all files from "extra" directory even when SOURCES specified.
RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \ RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \
RUN: -path-equivalence=/tmp,%S/Inputs -ignore-filename-regex='.*extra[/\\].*' \ 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: %S/Inputs/sources_specified/extra %S/Inputs/sources_specified/abs.h \
RUN: | FileCheck -check-prefix=REPORT_IGNORE_DIR_WITH_SOURCES %s RUN: | FileCheck -check-prefix=REPORT_IGNORE_DIR_WITH_SOURCES %s

View File

@ -88,6 +88,12 @@ private:
ArrayRef<ExpansionRecord> Expansions, ArrayRef<ExpansionRecord> Expansions,
const CoverageMapping &Coverage); 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. /// Create the source view of a particular function.
std::unique_ptr<SourceCoverageView> std::unique_ptr<SourceCoverageView>
createFunctionView(const FunctionRecord &Function, createFunctionView(const FunctionRecord &Function,
@ -268,15 +274,45 @@ void CodeCoverageTool::attachExpansionSubViews(
if (!SourceBuffer) if (!SourceBuffer)
continue; continue;
auto SubViewBranches = ExpansionCoverage.getBranches();
auto SubViewExpansions = ExpansionCoverage.getExpansions(); auto SubViewExpansions = ExpansionCoverage.getExpansions();
auto SubView = auto SubView =
SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(), SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
ViewOpts, std::move(ExpansionCoverage)); ViewOpts, std::move(ExpansionCoverage));
attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
attachBranchSubViews(*SubView, Expansion.Function.Name, SubViewBranches,
SourceBuffer.get(), ExpansionCoverage);
View.addExpansion(Expansion.Region, std::move(SubView)); 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> std::unique_ptr<SourceCoverageView>
CodeCoverageTool::createFunctionView(const FunctionRecord &Function, CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
const CoverageMapping &Coverage) { const CoverageMapping &Coverage) {
@ -287,11 +323,14 @@ CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
if (!SourceBuffer) if (!SourceBuffer)
return nullptr; return nullptr;
auto Branches = FunctionCoverage.getBranches();
auto Expansions = FunctionCoverage.getExpansions(); auto Expansions = FunctionCoverage.getExpansions();
auto View = SourceCoverageView::create(DC.demangle(Function.Name), auto View = SourceCoverageView::create(DC.demangle(Function.Name),
SourceBuffer.get(), ViewOpts, SourceBuffer.get(), ViewOpts,
std::move(FunctionCoverage)); std::move(FunctionCoverage));
attachExpansionSubViews(*View, Expansions, Coverage); attachExpansionSubViews(*View, Expansions, Coverage);
attachBranchSubViews(*View, DC.demangle(Function.Name), Branches,
SourceBuffer.get(), FunctionCoverage);
return View; return View;
} }
@ -306,10 +345,13 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile,
if (FileCoverage.empty()) if (FileCoverage.empty())
return nullptr; return nullptr;
auto Branches = FileCoverage.getBranches();
auto Expansions = FileCoverage.getExpansions(); auto Expansions = FileCoverage.getExpansions();
auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(), auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
ViewOpts, std::move(FileCoverage)); ViewOpts, std::move(FileCoverage));
attachExpansionSubViews(*View, Expansions, Coverage); attachExpansionSubViews(*View, Expansions, Coverage);
attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(),
FileCoverage);
if (!ViewOpts.ShowFunctionInstantiations) if (!ViewOpts.ShowFunctionInstantiations)
return View; return View;
@ -326,9 +368,12 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile,
if (Function->ExecutionCount > 0) { if (Function->ExecutionCount > 0) {
auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
auto SubViewExpansions = SubViewCoverage.getExpansions(); auto SubViewExpansions = SubViewCoverage.getExpansions();
auto SubViewBranches = SubViewCoverage.getBranches();
SubView = SourceCoverageView::create( SubView = SourceCoverageView::create(
Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage)); Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
attachBranchSubViews(*SubView, SourceFile, SubViewBranches,
SourceBuffer.get(), SubViewCoverage);
} }
unsigned FileID = Function->CountedRegions.front().FileID; 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::desc("Show region statistics in summary table"),
cl::init(true)); 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( cl::opt<bool> InstantiationSummary(
"show-instantiation-summary", cl::Optional, "show-instantiation-summary", cl::Optional,
cl::desc("Show instantiation statistics in summary table")); cl::desc("Show instantiation statistics in summary table"));
@ -788,6 +838,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
::exit(0); ::exit(0);
} }
ViewOpts.ShowBranchSummary = BranchSummary;
ViewOpts.ShowRegionSummary = RegionSummary; ViewOpts.ShowRegionSummary = RegionSummary;
ViewOpts.ShowInstantiationSummary = InstantiationSummary; ViewOpts.ShowInstantiationSummary = InstantiationSummary;
ViewOpts.ExportSummaryOnly = SummaryOnly; 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::desc("Show the execution counts for each region"),
cl::cat(ViewCategory)); 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( cl::opt<bool> ShowBestLineRegionsCounts(
"show-line-counts-or-regions", cl::Optional, "show-line-counts-or-regions", cl::Optional,
cl::desc("Show the execution counts for each line, or the execution " 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; !ShowRegions || ShowBestLineRegionsCounts;
ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
ViewOpts.ShowExpandedRegions = ShowExpansions; ViewOpts.ShowExpandedRegions = ShowExpansions;
ViewOpts.ShowBranchCounts =
ShowBranches == CoverageViewOptions::BranchOutputType::Count;
ViewOpts.ShowBranchPercents =
ShowBranches == CoverageViewOptions::BranchOutputType::Percent;
ViewOpts.ShowFunctionInstantiations = ShowInstantiations; ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
ViewOpts.ShowOutputDirectory = ShowOutputDirectory; ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
ViewOpts.TabSize = TabSize; ViewOpts.TabSize = TabSize;

View File

@ -18,6 +18,8 @@
// -- Export: dict => Json representation of one CoverageMapping // -- Export: dict => Json representation of one CoverageMapping
// -- Files: array => List of objects describing coverage for files // -- Files: array => List of objects describing coverage for files
// -- File: dict => Coverage for a single file // -- 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 // -- Segments: array => List of Segments contained in the file
// -- Segment: dict => Describes a segment of the file with a counter // -- Segment: dict => Describes a segment of the file with a counter
// -- Expansions: array => List of expansion records // -- Expansions: array => List of expansion records
@ -25,10 +27,13 @@
// -- CountedRegion: dict => The region to be expanded // -- CountedRegion: dict => The region to be expanded
// -- TargetRegions: array => List of Regions in the expansion // -- TargetRegions: array => List of Regions in the expansion
// -- CountedRegion: dict => Single Region 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 // -- Summary: dict => Object summarizing the coverage for this file
// -- LineCoverage: dict => Object summarizing line coverage // -- LineCoverage: dict => Object summarizing line coverage
// -- FunctionCoverage: dict => Object summarizing function coverage // -- FunctionCoverage: dict => Object summarizing function coverage
// -- RegionCoverage: dict => Object summarizing region coverage // -- RegionCoverage: dict => Object summarizing region coverage
// -- BranchCoverage: dict => Object summarizing branch coverage
// -- Functions: array => List of objects describing coverage for functions // -- Functions: array => List of objects describing coverage for functions
// -- Function: dict => Coverage info for a single function // -- Function: dict => Coverage info for a single function
// -- Filenames: array => List of filenames that the function relates to // -- Filenames: array => List of filenames that the function relates to
@ -37,6 +42,7 @@
// -- FunctionCoverage: dict => Object summarizing function coverage // -- FunctionCoverage: dict => Object summarizing function coverage
// -- InstantiationCoverage: dict => Object summarizing inst. coverage // -- InstantiationCoverage: dict => Object summarizing inst. coverage
// -- RegionCoverage: dict => Object summarizing region 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)}); 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 renderRegions(ArrayRef<coverage::CountedRegion> Regions) {
json::Array RegionArray; json::Array RegionArray;
for (const auto &Region : Regions) for (const auto &Region : Regions)
@ -91,13 +105,49 @@ json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) {
return RegionArray; 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( return json::Object(
{{"filenames", json::Array(Expansion.Function.Filenames)}, {{"filenames", json::Array(Expansion.Function.Filenames)},
// Mark the beginning and end of this expansion in the source file. // Mark the beginning and end of this expansion in the source file.
{"source_region", renderRegion(Expansion.Region)}, {"source_region", renderRegion(Expansion.Region)},
// Enumerate the coverage information for the expansion. // 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) { json::Object renderSummary(const FileCoverageSummary &Summary) {
@ -123,14 +173,22 @@ json::Object renderSummary(const FileCoverageSummary &Summary) {
{"covered", int64_t(Summary.RegionCoverage.getCovered())}, {"covered", int64_t(Summary.RegionCoverage.getCovered())},
{"notcovered", int64_t(Summary.RegionCoverage.getNumRegions() - {"notcovered", int64_t(Summary.RegionCoverage.getNumRegions() -
Summary.RegionCoverage.getCovered())}, 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) { const FileCoverageSummary &FileReport) {
json::Array ExpansionArray; json::Array ExpansionArray;
for (const auto &Expansion : FileCoverage.getExpansions()) for (const auto &Expansion : FileCoverage.getExpansions())
ExpansionArray.push_back(renderExpansion(Expansion)); ExpansionArray.push_back(renderExpansion(Coverage, Expansion));
return ExpansionArray; return ExpansionArray;
} }
@ -142,6 +200,14 @@ json::Array renderFileSegments(const coverage::CoverageData &FileCoverage,
return SegmentArray; 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, json::Object renderFile(const coverage::CoverageMapping &Coverage,
const std::string &Filename, const std::string &Filename,
const FileCoverageSummary &FileReport, const FileCoverageSummary &FileReport,
@ -151,8 +217,10 @@ json::Object renderFile(const coverage::CoverageMapping &Coverage,
// Calculate and render detailed coverage information for given file. // Calculate and render detailed coverage information for given file.
auto FileCoverage = Coverage.getCoverageForFile(Filename); auto FileCoverage = Coverage.getCoverageForFile(Filename);
File["segments"] = renderFileSegments(FileCoverage, FileReport); File["segments"] = renderFileSegments(FileCoverage, FileReport);
File["branches"] = renderFileBranches(FileCoverage, FileReport);
if (!Options.SkipExpansions) { if (!Options.SkipExpansions) {
File["expansions"] = renderFileExpansions(FileCoverage, FileReport); File["expansions"] =
renderFileExpansions(Coverage, FileCoverage, FileReport);
} }
} }
File["summary"] = renderSummary(FileReport); File["summary"] = renderSummary(FileReport);
@ -197,6 +265,7 @@ json::Array renderFunctions(
json::Object({{"name", F.Name}, json::Object({{"name", F.Name},
{"count", clamp_uint64_to_int64(F.ExecutionCount)}, {"count", clamp_uint64_to_int64(F.ExecutionCount)},
{"regions", renderRegions(F.CountedRegions)}, {"regions", renderRegions(F.CountedRegions)},
{"branches", renderBranchRegions(F.CountedBranchRegions)},
{"filenames", json::Array(F.Filenames)}})); {"filenames", json::Array(F.Filenames)}}));
return FunctionArray; return FunctionArray;
} }

View File

@ -26,6 +26,10 @@
// - "FNH:<number of functions hit>" // - "FNH:<number of functions hit>"
// - for each instrumented line: // - for each instrumented line:
// - "DA:<line number>,<execution count>[,<checksum>] // - "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>" // - "LH:<number of lines with non-zero execution count>"
// - "LF:<number of instrumented lines>" // - "LF:<number of instrumented lines>"
// - "end_of_record" // - "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) { void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n' OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n'
<< "LH:" << Summary.LineCoverage.getCovered() << '\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, void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
const std::string &Filename, const std::string &Filename,
const FileCoverageSummary &FileReport, bool ExportSummaryOnly, 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. // Calculate and render detailed coverage information for given file.
auto FileCoverage = Coverage.getCoverageForFile(Filename); auto FileCoverage = Coverage.getCoverageForFile(Filename);
renderLineExecutionCounts(OS, FileCoverage); renderLineExecutionCounts(OS, FileCoverage);
renderBranchExecutionCounts(OS, Coverage, FileCoverage);
} }
renderBranchSummary(OS, FileReport);
renderLineSummary(OS, FileReport); renderLineSummary(OS, FileReport);
OS << "end_of_record\n"; 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. // Specify the default column widths.
size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10, size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10, 16,
16, 16, 10, 12, 18, 10}; 16, 10, 12, 18, 10, 12, 18, 10};
size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8}; size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8, 10, 8, 8};
/// Adjust column widths to fit long file paths and function names. /// Adjust column widths to fit long file paths and function names.
void adjustColumnWidths(ArrayRef<StringRef> Files, void adjustColumnWidths(ArrayRef<StringRef> Files,
@ -239,6 +239,23 @@ void CoverageReport::render(const FileCoverageSummary &File,
<< '%'; << '%';
else else
OS << column("-", FileReportColumns[12], Column::RightAlignment); 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"; OS << "\n";
} }
@ -273,6 +290,19 @@ void CoverageReport::render(const FunctionCoverageSummary &Function,
<< format("%*.2f", FunctionReportColumns[6] - 1, << format("%*.2f", FunctionReportColumns[6] - 1,
Function.LineCoverage.getPercentCovered()) 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"; OS << "\n";
} }
@ -301,6 +331,10 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
<< column("Lines", FunctionReportColumns[4], Column::RightAlignment) << column("Lines", FunctionReportColumns[4], Column::RightAlignment)
<< column("Miss", FunctionReportColumns[5], Column::RightAlignment) << column("Miss", FunctionReportColumns[5], Column::RightAlignment)
<< column("Cover", FunctionReportColumns[6], 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"; OS << "\n";
renderDivider(FunctionReportColumns, OS); renderDivider(FunctionReportColumns, OS);
OS << "\n"; OS << "\n";
@ -310,6 +344,7 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
++Totals.ExecutionCount; ++Totals.ExecutionCount;
Totals.RegionCoverage += Function.RegionCoverage; Totals.RegionCoverage += Function.RegionCoverage;
Totals.LineCoverage += Function.LineCoverage; Totals.LineCoverage += Function.LineCoverage;
Totals.BranchCoverage += Function.BranchCoverage;
render(Function, DC, OS); render(Function, DC, OS);
} }
if (Totals.ExecutionCount) { if (Totals.ExecutionCount) {
@ -420,7 +455,13 @@ void CoverageReport::renderFileReports(
<< column("Executed", FileReportColumns[9], Column::RightAlignment); << column("Executed", FileReportColumns[9], Column::RightAlignment);
OS << column("Lines", FileReportColumns[10], Column::RightAlignment) OS << column("Lines", FileReportColumns[10], Column::RightAlignment)
<< column("Missed Lines", FileReportColumns[11], 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); renderDivider(FileReportColumns, OS);
OS << "\n"; OS << "\n";

View File

@ -16,6 +16,34 @@
using namespace llvm; using namespace llvm;
using namespace coverage; 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
FunctionCoverageSummary::get(const CoverageMapping &CM, FunctionCoverageSummary::get(const CoverageMapping &CM,
const coverage::FunctionRecord &Function) { const coverage::FunctionRecord &Function) {
@ -40,10 +68,16 @@ FunctionCoverageSummary::get(const CoverageMapping &CM,
++CoveredLines; ++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( return FunctionCoverageSummary(
Function.Name, Function.ExecutionCount, Function.Name, Function.ExecutionCount,
RegionCoverageInfo(CoveredRegions, NumCodeRegions), RegionCoverageInfo(CoveredRegions, NumCodeRegions),
LineCoverageInfo(CoveredLines, NumLines)); LineCoverageInfo(CoveredLines, NumLines),
BranchCoverageInfo(CoveredBranches, NumBranches));
} }
FunctionCoverageSummary FunctionCoverageSummary
@ -62,9 +96,15 @@ FunctionCoverageSummary::get(const InstantiationGroup &Group,
Summary.ExecutionCount = Group.getTotalExecutionCount(); Summary.ExecutionCount = Group.getTotalExecutionCount();
Summary.RegionCoverage = Summaries[0].RegionCoverage; Summary.RegionCoverage = Summaries[0].RegionCoverage;
Summary.LineCoverage = Summaries[0].LineCoverage; Summary.LineCoverage = Summaries[0].LineCoverage;
Summary.BranchCoverage = Summaries[0].BranchCoverage;
for (const auto &FCS : Summaries.drop_front()) { for (const auto &FCS : Summaries.drop_front()) {
Summary.RegionCoverage.merge(FCS.RegionCoverage); Summary.RegionCoverage.merge(FCS.RegionCoverage);
Summary.LineCoverage.merge(FCS.LineCoverage); 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; 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. /// Provides information about function coverage for a file.
class FunctionCoverageInfo { class FunctionCoverageInfo {
/// The number of functions that were executed. /// The number of functions that were executed.
@ -147,15 +183,19 @@ struct FunctionCoverageSummary {
uint64_t ExecutionCount; uint64_t ExecutionCount;
RegionCoverageInfo RegionCoverage; RegionCoverageInfo RegionCoverage;
LineCoverageInfo LineCoverage; LineCoverageInfo LineCoverage;
BranchCoverageInfo BranchCoverage;
FunctionCoverageSummary(const std::string &Name) 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, FunctionCoverageSummary(const std::string &Name, uint64_t ExecutionCount,
const RegionCoverageInfo &RegionCoverage, const RegionCoverageInfo &RegionCoverage,
const LineCoverageInfo &LineCoverage) const LineCoverageInfo &LineCoverage,
const BranchCoverageInfo &BranchCoverage)
: Name(Name), ExecutionCount(ExecutionCount), : Name(Name), ExecutionCount(ExecutionCount),
RegionCoverage(RegionCoverage), LineCoverage(LineCoverage) {} RegionCoverage(RegionCoverage), LineCoverage(LineCoverage),
BranchCoverage(BranchCoverage) {}
/// Compute the code coverage summary for the given function coverage /// Compute the code coverage summary for the given function coverage
/// mapping record. /// mapping record.
@ -174,6 +214,7 @@ struct FileCoverageSummary {
StringRef Name; StringRef Name;
RegionCoverageInfo RegionCoverage; RegionCoverageInfo RegionCoverage;
LineCoverageInfo LineCoverage; LineCoverageInfo LineCoverage;
BranchCoverageInfo BranchCoverage;
FunctionCoverageInfo FunctionCoverage; FunctionCoverageInfo FunctionCoverage;
FunctionCoverageInfo InstantiationCoverage; FunctionCoverageInfo InstantiationCoverage;
@ -185,6 +226,7 @@ struct FileCoverageSummary {
RegionCoverage += RHS.RegionCoverage; RegionCoverage += RHS.RegionCoverage;
LineCoverage += RHS.LineCoverage; LineCoverage += RHS.LineCoverage;
FunctionCoverage += RHS.FunctionCoverage; FunctionCoverage += RHS.FunctionCoverage;
BranchCoverage += RHS.BranchCoverage;
InstantiationCoverage += RHS.InstantiationCoverage; InstantiationCoverage += RHS.InstantiationCoverage;
return *this; return *this;
} }
@ -192,6 +234,7 @@ struct FileCoverageSummary {
void addFunction(const FunctionCoverageSummary &Function) { void addFunction(const FunctionCoverageSummary &Function) {
RegionCoverage += Function.RegionCoverage; RegionCoverage += Function.RegionCoverage;
LineCoverage += Function.LineCoverage; LineCoverage += Function.LineCoverage;
BranchCoverage += Function.BranchCoverage;
FunctionCoverage.addFunction(/*Covered=*/Function.ExecutionCount > 0); FunctionCoverage.addFunction(/*Covered=*/Function.ExecutionCount > 0);
} }

View File

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

View File

@ -132,7 +132,8 @@ bool SourceCoverageView::shouldRenderRegionMarkers(
} }
bool SourceCoverageView::hasSubViews() const { bool SourceCoverageView::hasSubViews() const {
return !ExpansionSubViews.empty() || !InstantiationSubViews.empty(); return !ExpansionSubViews.empty() || !InstantiationSubViews.empty() ||
!BranchSubViews.empty();
} }
std::unique_ptr<SourceCoverageView> std::unique_ptr<SourceCoverageView>
@ -167,6 +168,12 @@ void SourceCoverageView::addExpansion(
ExpansionSubViews.emplace_back(Region, std::move(View)); 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( void SourceCoverageView::addInstantiation(
StringRef FunctionName, unsigned Line, StringRef FunctionName, unsigned Line,
std::unique_ptr<SourceCoverageView> View) { std::unique_ptr<SourceCoverageView> View) {
@ -187,14 +194,17 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
renderTableHeader(OS, (ViewDepth > 0) ? 0 : getFirstUncoveredLineNo(), renderTableHeader(OS, (ViewDepth > 0) ? 0 : getFirstUncoveredLineNo(),
ViewDepth); ViewDepth);
// We need the expansions and instantiations sorted so we can go through them // We need the expansions, instantiations, and branches sorted so we can go
// while we iterate lines. // through them while we iterate lines.
llvm::stable_sort(ExpansionSubViews); llvm::stable_sort(ExpansionSubViews);
llvm::stable_sort(InstantiationSubViews); llvm::stable_sort(InstantiationSubViews);
llvm::stable_sort(BranchSubViews);
auto NextESV = ExpansionSubViews.begin(); auto NextESV = ExpansionSubViews.begin();
auto EndESV = ExpansionSubViews.end(); auto EndESV = ExpansionSubViews.end();
auto NextISV = InstantiationSubViews.begin(); auto NextISV = InstantiationSubViews.begin();
auto EndISV = InstantiationSubViews.end(); auto EndISV = InstantiationSubViews.end();
auto NextBRV = BranchSubViews.begin();
auto EndBRV = BranchSubViews.end();
// Get the coverage information for the file. // Get the coverage information for the file.
auto StartSegment = CoverageInfo.begin(); auto StartSegment = CoverageInfo.begin();
@ -234,7 +244,7 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
if (shouldRenderRegionMarkers(*LCI)) if (shouldRenderRegionMarkers(*LCI))
renderRegionMarkers(OS, *LCI, ViewDepth); renderRegionMarkers(OS, *LCI, ViewDepth);
// Show the expansions and instantiations for this line. // Show the expansions, instantiations, and branches for this line.
bool RenderedSubView = false; bool RenderedSubView = false;
for (; NextESV != EndESV && NextESV->getLine() == LI.line_number(); for (; NextESV != EndESV && NextESV->getLine() == LI.line_number();
++NextESV) { ++NextESV) {
@ -257,6 +267,11 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
renderInstantiationView(OS, *NextISV, ViewDepth + 1); renderInstantiationView(OS, *NextISV, ViewDepth + 1);
RenderedSubView = true; RenderedSubView = true;
} }
for (; NextBRV != EndBRV && NextBRV->Line == LI.line_number(); ++NextBRV) {
renderViewDivider(OS, ViewDepth + 1);
renderBranchView(OS, *NextBRV, ViewDepth + 1);
RenderedSubView = true;
}
if (RenderedSubView) if (RenderedSubView)
renderViewDivider(OS, ViewDepth + 1); renderViewDivider(OS, ViewDepth + 1);
renderLineSuffix(OS, ViewDepth); 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. /// A file manager that handles format-aware file creation.
class CoveragePrinter { class CoveragePrinter {
public: public:
@ -140,6 +157,9 @@ class SourceCoverageView {
/// A container for all expansions (e.g macros) in the source on display. /// A container for all expansions (e.g macros) in the source on display.
std::vector<ExpansionView> ExpansionSubViews; 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 /// A container for all instantiations (e.g template functions) in the source
/// on display. /// on display.
std::vector<InstantiationView> InstantiationSubViews; std::vector<InstantiationView> InstantiationSubViews;
@ -209,6 +229,10 @@ protected:
virtual void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, virtual void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
unsigned ViewDepth) = 0; 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 /// Render \p Title, a project title if one is available, and the
/// created time. /// created time.
virtual void renderTitle(raw_ostream &OS, StringRef CellText) = 0; virtual void renderTitle(raw_ostream &OS, StringRef CellText) = 0;
@ -255,6 +279,10 @@ public:
void addInstantiation(StringRef FunctionName, unsigned Line, void addInstantiation(StringRef FunctionName, unsigned Line,
std::unique_ptr<SourceCoverageView> View); 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 /// Print the code coverage information for a specific portion of a
/// source file to the output stream. /// source file to the output stream.
void print(raw_ostream &OS, bool WholeFile, bool ShowSourceName, 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")); Columns.emplace_back(tag("td", "Line Coverage", "column-entry-bold"));
if (Opts.ShowRegionSummary) if (Opts.ShowRegionSummary)
Columns.emplace_back(tag("td", "Region Coverage", "column-entry-bold")); 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(), "")); OS << tag("tr", join(Columns.begin(), Columns.end(), ""));
} }
@ -378,6 +380,10 @@ void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF,
AddCoverageTripleToColumn(FCS.RegionCoverage.getCovered(), AddCoverageTripleToColumn(FCS.RegionCoverage.getCovered(),
FCS.RegionCoverage.getNumRegions(), FCS.RegionCoverage.getNumRegions(),
FCS.RegionCoverage.getPercentCovered()); FCS.RegionCoverage.getPercentCovered());
if (Opts.ShowBranchSummary)
AddCoverageTripleToColumn(FCS.BranchCoverage.getCovered(),
FCS.BranchCoverage.getNumBranches(),
FCS.BranchCoverage.getPercentCovered());
if (IsTotals) if (IsTotals)
OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row-bold"); OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row-bold");
@ -650,6 +656,72 @@ void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS,
OS << EndExpansionDiv; 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, void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS,
InstantiationView &ISV, InstantiationView &ISV,
unsigned ViewDepth) { unsigned ViewDepth) {

View File

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

View File

@ -10,11 +10,12 @@
/// ///
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "CoverageReport.h"
#include "SourceCoverageViewText.h" #include "SourceCoverageViewText.h"
#include "CoverageReport.h"
#include "llvm/ADT/Optional.h" #include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Format.h"
using namespace llvm; using namespace llvm;
@ -222,6 +223,53 @@ void SourceCoverageViewText::renderExpansionView(raw_ostream &OS,
/*ShowTitle=*/false, ViewDepth + 1); /*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, void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,
InstantiationView &ISV, InstantiationView &ISV,
unsigned ViewDepth) { unsigned ViewDepth) {

View File

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