Reland "[Coverage][llvm-cov] Enable MC/DC Support in LLVM Source-based Code Coverage (2/3)"
Part 2 of 3. This includes the Visualization and Evaluation components. Differential Revision: https://reviews.llvm.org/D138847
This commit is contained in:
@@ -1623,8 +1623,12 @@ static void dump(llvm::raw_ostream &OS, StringRef FunctionName,
|
||||
OS << "Gap,";
|
||||
break;
|
||||
case CounterMappingRegion::BranchRegion:
|
||||
case CounterMappingRegion::MCDCBranchRegion:
|
||||
OS << "Branch,";
|
||||
break;
|
||||
case CounterMappingRegion::MCDCDecisionRegion:
|
||||
OS << "Decision,";
|
||||
break;
|
||||
}
|
||||
|
||||
OS << "File " << R.FileID << ", " << R.LineStart << ":" << R.ColumnStart
|
||||
|
||||
@@ -222,6 +222,11 @@ OPTIONS
|
||||
Show coverage for branch conditions in terms of either count or percentage.
|
||||
The supported views are: "count", "percent".
|
||||
|
||||
.. option:: -show-mcdc
|
||||
|
||||
Show modified condition/decision coverage (MC/DC) for each applicable boolean
|
||||
expression.
|
||||
|
||||
.. option:: -show-line-counts
|
||||
|
||||
Show the execution counts for each line. Defaults to true, unless another
|
||||
@@ -426,6 +431,10 @@ OPTIONS
|
||||
|
||||
Show statistics for all branch conditions. Defaults to true.
|
||||
|
||||
.. option:: -show-mcdc-summary
|
||||
|
||||
Show MC/DC statistics. Defaults to false.
|
||||
|
||||
.. option:: -show-functions
|
||||
|
||||
Show coverage summaries for each function. Defaults to false.
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#define LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPING_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/BitVector.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
@@ -33,6 +34,7 @@
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <tuple>
|
||||
@@ -237,7 +239,27 @@ struct CounterMappingRegion {
|
||||
/// 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
|
||||
BranchRegion,
|
||||
|
||||
/// A DecisionRegion represents a top-level boolean expression and is
|
||||
/// associated with a variable length bitmap index and condition number.
|
||||
MCDCDecisionRegion,
|
||||
|
||||
/// A Branch Region can be extended to include IDs to facilitate MC/DC.
|
||||
MCDCBranchRegion
|
||||
};
|
||||
|
||||
using MCDCConditionID = unsigned int;
|
||||
struct MCDCParameters {
|
||||
/// Byte Index of Bitmap Coverage Object for a Decision Region.
|
||||
unsigned BitmapIdx = 0;
|
||||
|
||||
/// Number of Conditions used for a Decision Region.
|
||||
unsigned NumConditions = 0;
|
||||
|
||||
/// IDs used to represent a branch region and other branch regions
|
||||
/// evaluated based on True and False branches.
|
||||
MCDCConditionID ID = 0, TrueID = 0, FalseID = 0;
|
||||
};
|
||||
|
||||
/// Primary Counter that is also used for Branch Regions (TrueCount).
|
||||
@@ -246,8 +268,13 @@ struct CounterMappingRegion {
|
||||
/// Secondary Counter used for Branch Regions (FalseCount).
|
||||
Counter FalseCount;
|
||||
|
||||
unsigned FileID, ExpandedFileID;
|
||||
/// Parameters used for Modified Condition/Decision Coverage
|
||||
MCDCParameters MCDCParams;
|
||||
|
||||
unsigned FileID = 0;
|
||||
unsigned ExpandedFileID = 0;
|
||||
unsigned LineStart, ColumnStart, LineEnd, ColumnEnd;
|
||||
|
||||
RegionKind Kind;
|
||||
|
||||
CounterMappingRegion(Counter Count, unsigned FileID, unsigned ExpandedFileID,
|
||||
@@ -257,15 +284,24 @@ struct CounterMappingRegion {
|
||||
LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd),
|
||||
ColumnEnd(ColumnEnd), Kind(Kind) {}
|
||||
|
||||
CounterMappingRegion(Counter Count, Counter FalseCount, unsigned FileID,
|
||||
CounterMappingRegion(Counter Count, Counter FalseCount,
|
||||
MCDCParameters MCDCParams, unsigned FileID,
|
||||
unsigned ExpandedFileID, unsigned LineStart,
|
||||
unsigned ColumnStart, unsigned LineEnd,
|
||||
unsigned ColumnEnd, RegionKind Kind)
|
||||
: Count(Count), FalseCount(FalseCount), FileID(FileID),
|
||||
ExpandedFileID(ExpandedFileID), LineStart(LineStart),
|
||||
: Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams),
|
||||
FileID(FileID), ExpandedFileID(ExpandedFileID), LineStart(LineStart),
|
||||
ColumnStart(ColumnStart), LineEnd(LineEnd), ColumnEnd(ColumnEnd),
|
||||
Kind(Kind) {}
|
||||
|
||||
CounterMappingRegion(MCDCParameters MCDCParams, unsigned FileID,
|
||||
unsigned ExpandedFileID, unsigned LineStart,
|
||||
unsigned ColumnStart, unsigned LineEnd,
|
||||
unsigned ColumnEnd, RegionKind Kind)
|
||||
: MCDCParams(MCDCParams), ExpandedFileID(ExpandedFileID),
|
||||
LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd),
|
||||
ColumnEnd(ColumnEnd), Kind(Kind) {}
|
||||
|
||||
static CounterMappingRegion
|
||||
makeRegion(Counter Count, unsigned FileID, unsigned LineStart,
|
||||
unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) {
|
||||
@@ -299,8 +335,27 @@ struct 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);
|
||||
return CounterMappingRegion(Count, FalseCount, MCDCParameters(), FileID, 0,
|
||||
LineStart, ColumnStart, LineEnd, ColumnEnd,
|
||||
BranchRegion);
|
||||
}
|
||||
|
||||
static CounterMappingRegion
|
||||
makeBranchRegion(Counter Count, Counter FalseCount, MCDCParameters MCDCParams,
|
||||
unsigned FileID, unsigned LineStart, unsigned ColumnStart,
|
||||
unsigned LineEnd, unsigned ColumnEnd) {
|
||||
return CounterMappingRegion(Count, FalseCount, MCDCParams, FileID, 0,
|
||||
LineStart, ColumnStart, LineEnd, ColumnEnd,
|
||||
MCDCParams.ID == 0 ? BranchRegion
|
||||
: MCDCBranchRegion);
|
||||
}
|
||||
|
||||
static CounterMappingRegion
|
||||
makeDecisionRegion(MCDCParameters MCDCParams, unsigned FileID,
|
||||
unsigned LineStart, unsigned ColumnStart, unsigned LineEnd,
|
||||
unsigned ColumnEnd) {
|
||||
return CounterMappingRegion(MCDCParams, FileID, 0, LineStart, ColumnStart,
|
||||
LineEnd, ColumnEnd, MCDCDecisionRegion);
|
||||
}
|
||||
|
||||
inline LineColPair startLoc() const {
|
||||
@@ -326,11 +381,189 @@ struct CountedRegion : public CounterMappingRegion {
|
||||
FalseExecutionCount(FalseExecutionCount), Folded(false) {}
|
||||
};
|
||||
|
||||
/// MCDC Record grouping all information together.
|
||||
struct MCDCRecord {
|
||||
/// CondState represents the evaluation of a condition in an executed test
|
||||
/// vector, which can be True or False. A DontCare is used to mask an
|
||||
/// unevaluatable condition resulting from short-circuit behavior of logical
|
||||
/// operators in languages like C/C++. When comparing the evaluation of a
|
||||
/// condition across executed test vectors, comparisons against a DontCare
|
||||
/// are effectively ignored.
|
||||
enum CondState { MCDC_DontCare = -1, MCDC_False = 0, MCDC_True = 1 };
|
||||
|
||||
using TestVector = llvm::SmallVector<CondState>;
|
||||
using TestVectors = llvm::SmallVector<TestVector>;
|
||||
using BoolVector = llvm::SmallVector<bool>;
|
||||
using TVRowPair = std::pair<unsigned, unsigned>;
|
||||
using TVPairMap = llvm::DenseMap<unsigned, TVRowPair>;
|
||||
using CondIDMap = llvm::DenseMap<unsigned, unsigned>;
|
||||
using LineColPairMap = llvm::DenseMap<unsigned, LineColPair>;
|
||||
|
||||
private:
|
||||
CounterMappingRegion Region;
|
||||
TestVectors TV;
|
||||
TVPairMap IndependencePairs;
|
||||
BoolVector Folded;
|
||||
CondIDMap PosToID;
|
||||
LineColPairMap CondLoc;
|
||||
|
||||
public:
|
||||
MCDCRecord(CounterMappingRegion Region, TestVectors TV,
|
||||
TVPairMap IndependencePairs, BoolVector Folded, CondIDMap PosToID,
|
||||
LineColPairMap CondLoc)
|
||||
: Region(Region), TV(TV), IndependencePairs(IndependencePairs),
|
||||
Folded(Folded), PosToID(PosToID), CondLoc(CondLoc){};
|
||||
|
||||
CounterMappingRegion getDecisionRegion() const { return Region; }
|
||||
unsigned getNumConditions() const {
|
||||
assert(Region.MCDCParams.NumConditions != 0 &&
|
||||
"In MC/DC, NumConditions should never be zero!");
|
||||
return Region.MCDCParams.NumConditions;
|
||||
}
|
||||
unsigned getNumTestVectors() const { return TV.size(); }
|
||||
bool isCondFolded(unsigned Condition) const { return Folded[Condition]; }
|
||||
|
||||
/// Return the evaluation of a condition (indicated by Condition) in an
|
||||
/// executed test vector (indicated by TestVectorIndex), which will be True,
|
||||
/// False, or DontCare if the condition is unevaluatable. Because condition
|
||||
/// IDs are not associated based on their position in the expression,
|
||||
/// accessing conditions in the TestVectors requires a translation from a
|
||||
/// ordinal position to actual condition ID. This is done via PosToID[].
|
||||
CondState getTVCondition(unsigned TestVectorIndex, unsigned Condition) {
|
||||
return TV[TestVectorIndex][PosToID[Condition]];
|
||||
}
|
||||
|
||||
/// Return the Result evaluation for an executed test vector.
|
||||
/// See MCDCRecordProcessor::RecordTestVector().
|
||||
CondState getTVResult(unsigned TestVectorIndex) {
|
||||
return TV[TestVectorIndex][getNumConditions()];
|
||||
}
|
||||
|
||||
/// Determine whether a given condition (indicated by Condition) is covered
|
||||
/// by an Independence Pair. Because condition IDs are not associated based
|
||||
/// on their position in the expression, accessing conditions in the
|
||||
/// TestVectors requires a translation from a ordinal position to actual
|
||||
/// condition ID. This is done via PosToID[].
|
||||
bool isConditionIndependencePairCovered(unsigned Condition) const {
|
||||
auto It = PosToID.find(Condition);
|
||||
if (It != PosToID.end())
|
||||
return (IndependencePairs.find(It->second) != IndependencePairs.end());
|
||||
llvm_unreachable("Condition ID without an Ordinal mapping");
|
||||
}
|
||||
|
||||
/// Return the Independence Pair that covers the given condition. Because
|
||||
/// condition IDs are not associated based on their position in the
|
||||
/// expression, accessing conditions in the TestVectors requires a
|
||||
/// translation from a ordinal position to actual condition ID. This is done
|
||||
/// via PosToID[].
|
||||
TVRowPair getConditionIndependencePair(unsigned Condition) {
|
||||
assert(isConditionIndependencePairCovered(Condition));
|
||||
return IndependencePairs[PosToID[Condition]];
|
||||
}
|
||||
|
||||
float getPercentCovered() const {
|
||||
unsigned Folded = 0;
|
||||
unsigned Covered = 0;
|
||||
for (unsigned C = 0; C < getNumConditions(); C++) {
|
||||
if (isCondFolded(C))
|
||||
Folded++;
|
||||
else if (isConditionIndependencePairCovered(C))
|
||||
Covered++;
|
||||
}
|
||||
|
||||
unsigned Total = getNumConditions() - Folded;
|
||||
if (Total == 0)
|
||||
return 0.0;
|
||||
return (static_cast<double>(Covered) / static_cast<double>(Total)) * 100.0;
|
||||
}
|
||||
|
||||
std::string getConditionHeaderString(unsigned Condition) {
|
||||
std::ostringstream OS;
|
||||
OS << "Condition C" << Condition + 1 << " --> (";
|
||||
OS << CondLoc[Condition].first << ":" << CondLoc[Condition].second;
|
||||
OS << ")\n";
|
||||
return OS.str();
|
||||
}
|
||||
|
||||
std::string getTestVectorHeaderString() const {
|
||||
std::ostringstream OS;
|
||||
if (getNumTestVectors() == 0) {
|
||||
OS << "None.\n";
|
||||
return OS.str();
|
||||
}
|
||||
const auto NumConditions = getNumConditions();
|
||||
for (unsigned I = 0; I < NumConditions; I++) {
|
||||
OS << "C" << I + 1;
|
||||
if (I != NumConditions - 1)
|
||||
OS << ", ";
|
||||
}
|
||||
OS << " Result\n";
|
||||
return OS.str();
|
||||
}
|
||||
|
||||
std::string getTestVectorString(unsigned TestVectorIndex) {
|
||||
assert(TestVectorIndex < getNumTestVectors() &&
|
||||
"TestVector index out of bounds!");
|
||||
std::ostringstream OS;
|
||||
const auto NumConditions = getNumConditions();
|
||||
// Add individual condition values to the string.
|
||||
OS << " " << TestVectorIndex + 1 << " { ";
|
||||
for (unsigned Condition = 0; Condition < NumConditions; Condition++) {
|
||||
if (isCondFolded(Condition))
|
||||
OS << "C";
|
||||
else {
|
||||
switch (getTVCondition(TestVectorIndex, Condition)) {
|
||||
case MCDCRecord::MCDC_DontCare:
|
||||
OS << "-";
|
||||
break;
|
||||
case MCDCRecord::MCDC_True:
|
||||
OS << "T";
|
||||
break;
|
||||
case MCDCRecord::MCDC_False:
|
||||
OS << "F";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Condition != NumConditions - 1)
|
||||
OS << ", ";
|
||||
}
|
||||
|
||||
// Add result value to the string.
|
||||
OS << " = ";
|
||||
if (getTVResult(TestVectorIndex) == MCDC_True)
|
||||
OS << "T";
|
||||
else
|
||||
OS << "F";
|
||||
OS << " }\n";
|
||||
|
||||
return OS.str();
|
||||
}
|
||||
|
||||
std::string getConditionCoverageString(unsigned Condition) {
|
||||
assert(Condition < getNumConditions() &&
|
||||
"Condition index is out of bounds!");
|
||||
std::ostringstream OS;
|
||||
|
||||
OS << " C" << Condition + 1 << "-Pair: ";
|
||||
if (isCondFolded(Condition)) {
|
||||
OS << "constant folded\n";
|
||||
} else if (isConditionIndependencePairCovered(Condition)) {
|
||||
TVRowPair rows = getConditionIndependencePair(Condition);
|
||||
OS << "covered: (" << rows.first << ",";
|
||||
OS << rows.second << ")\n";
|
||||
} else
|
||||
OS << "not covered\n";
|
||||
|
||||
return OS.str();
|
||||
}
|
||||
};
|
||||
|
||||
/// A Counter mapping context is used to connect the counters, expressions
|
||||
/// and the obtained counter values.
|
||||
class CounterMappingContext {
|
||||
ArrayRef<CounterExpression> Expressions;
|
||||
ArrayRef<uint64_t> CounterValues;
|
||||
ArrayRef<uint8_t> BitmapBytes;
|
||||
|
||||
public:
|
||||
CounterMappingContext(ArrayRef<CounterExpression> Expressions,
|
||||
@@ -338,6 +571,7 @@ public:
|
||||
: Expressions(Expressions), CounterValues(CounterValues) {}
|
||||
|
||||
void setCounts(ArrayRef<uint64_t> Counts) { CounterValues = Counts; }
|
||||
void setBitmapBytes(ArrayRef<uint8_t> Bytes) { BitmapBytes = Bytes; }
|
||||
|
||||
void dump(const Counter &C, raw_ostream &OS) const;
|
||||
void dump(const Counter &C) const { dump(C, dbgs()); }
|
||||
@@ -346,6 +580,17 @@ public:
|
||||
/// counter was executed.
|
||||
Expected<int64_t> evaluate(const Counter &C) const;
|
||||
|
||||
/// Return the number of times that a region of code associated with this
|
||||
/// counter was executed.
|
||||
Expected<BitVector>
|
||||
evaluateBitmap(const CounterMappingRegion *MCDCDecision) const;
|
||||
|
||||
/// Return an MCDC record that indicates executed test vectors and condition
|
||||
/// pairs.
|
||||
Expected<MCDCRecord>
|
||||
evaluateMCDCRegion(CounterMappingRegion Region, BitVector Bitmap,
|
||||
ArrayRef<CounterMappingRegion> Branches);
|
||||
|
||||
unsigned getMaxCounterID(const Counter &C) const;
|
||||
};
|
||||
|
||||
@@ -364,6 +609,8 @@ struct FunctionRecord {
|
||||
std::vector<CountedRegion> CountedRegions;
|
||||
/// Branch Regions in the function along with their counts.
|
||||
std::vector<CountedRegion> CountedBranchRegions;
|
||||
/// MCDC Records record a DecisionRegion and associated BranchRegions.
|
||||
std::vector<MCDCRecord> MCDCRecords;
|
||||
/// The number of times this function was executed.
|
||||
uint64_t ExecutionCount = 0;
|
||||
|
||||
@@ -373,9 +620,12 @@ struct FunctionRecord {
|
||||
FunctionRecord(FunctionRecord &&FR) = default;
|
||||
FunctionRecord &operator=(FunctionRecord &&) = default;
|
||||
|
||||
void pushMCDCRecord(MCDCRecord Record) { MCDCRecords.push_back(Record); }
|
||||
|
||||
void pushRegion(CounterMappingRegion Region, uint64_t Count,
|
||||
uint64_t FalseCount) {
|
||||
if (Region.Kind == CounterMappingRegion::BranchRegion) {
|
||||
if (Region.Kind == CounterMappingRegion::BranchRegion ||
|
||||
Region.Kind == CounterMappingRegion::MCDCBranchRegion) {
|
||||
CountedBranchRegions.emplace_back(Region, Count, FalseCount);
|
||||
// If both counters are hard-coded to zero, then this region represents a
|
||||
// constant-folded branch.
|
||||
@@ -546,6 +796,7 @@ class CoverageData {
|
||||
std::vector<CoverageSegment> Segments;
|
||||
std::vector<ExpansionRecord> Expansions;
|
||||
std::vector<CountedRegion> BranchRegions;
|
||||
std::vector<MCDCRecord> MCDCRecords;
|
||||
|
||||
public:
|
||||
CoverageData() = default;
|
||||
@@ -572,6 +823,9 @@ public:
|
||||
|
||||
/// Branches that can be further processed.
|
||||
ArrayRef<CountedRegion> getBranches() const { return BranchRegions; }
|
||||
|
||||
/// MCDC Records that can be further processed.
|
||||
ArrayRef<MCDCRecord> getMCDCRecords() const { return MCDCRecords; }
|
||||
};
|
||||
|
||||
/// The mapping of profile information to coverage data.
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
@@ -221,6 +222,264 @@ Expected<int64_t> CounterMappingContext::evaluate(const Counter &C) const {
|
||||
return LastPoppedValue;
|
||||
}
|
||||
|
||||
Expected<BitVector> CounterMappingContext::evaluateBitmap(
|
||||
const CounterMappingRegion *MCDCDecision) const {
|
||||
unsigned ID = MCDCDecision->MCDCParams.BitmapIdx;
|
||||
unsigned NC = MCDCDecision->MCDCParams.NumConditions;
|
||||
unsigned SizeInBits = llvm::alignTo(uint64_t(1) << NC, CHAR_BIT);
|
||||
unsigned SizeInBytes = SizeInBits / CHAR_BIT;
|
||||
|
||||
ArrayRef<uint8_t> Bytes(&BitmapBytes[ID], SizeInBytes);
|
||||
|
||||
// Mask each bitmap byte into the BitVector. Go in reverse so that the
|
||||
// bitvector can just be shifted over by one byte on each iteration.
|
||||
BitVector Result(SizeInBits, false);
|
||||
for (auto Byte = std::rbegin(Bytes); Byte != std::rend(Bytes); ++Byte) {
|
||||
uint32_t Data = *Byte;
|
||||
Result <<= CHAR_BIT;
|
||||
Result.setBitsInMask(&Data, 1);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
class MCDCRecordProcessor {
|
||||
/// A bitmap representing the executed test vectors for a boolean expression.
|
||||
/// Each index of the bitmap corresponds to a possible test vector. An index
|
||||
/// with a bit value of '1' indicates that the corresponding Test Vector
|
||||
/// identified by that index was executed.
|
||||
BitVector &ExecutedTestVectorBitmap;
|
||||
|
||||
/// Decision Region to which the ExecutedTestVectorBitmap applies.
|
||||
CounterMappingRegion &Region;
|
||||
|
||||
/// Array of branch regions corresponding each conditions in the boolean
|
||||
/// expression.
|
||||
ArrayRef<CounterMappingRegion> Branches;
|
||||
|
||||
/// Total number of conditions in the boolean expression.
|
||||
unsigned NumConditions;
|
||||
|
||||
/// Mapping of a condition ID to its corresponding branch region.
|
||||
llvm::DenseMap<unsigned, const CounterMappingRegion *> Map;
|
||||
|
||||
/// Vector used to track whether a condition is constant folded.
|
||||
MCDCRecord::BoolVector Folded;
|
||||
|
||||
/// Mapping of calculated MC/DC Independence Pairs for each condition.
|
||||
MCDCRecord::TVPairMap IndependencePairs;
|
||||
|
||||
/// Total number of possible Test Vectors for the boolean expression.
|
||||
MCDCRecord::TestVectors TestVectors;
|
||||
|
||||
/// Actual executed Test Vectors for the boolean expression, based on
|
||||
/// ExecutedTestVectorBitmap.
|
||||
MCDCRecord::TestVectors ExecVectors;
|
||||
|
||||
public:
|
||||
MCDCRecordProcessor(BitVector &Bitmap, CounterMappingRegion &Region,
|
||||
ArrayRef<CounterMappingRegion> Branches)
|
||||
: ExecutedTestVectorBitmap(Bitmap), Region(Region), Branches(Branches),
|
||||
NumConditions(Region.MCDCParams.NumConditions),
|
||||
Folded(NumConditions, false), IndependencePairs(NumConditions),
|
||||
TestVectors(pow(2, NumConditions)) {}
|
||||
|
||||
private:
|
||||
void recordTestVector(MCDCRecord::TestVector &TV,
|
||||
MCDCRecord::CondState Result) {
|
||||
// Calculate an index that is used to identify the test vector in a vector
|
||||
// of test vectors. This index also corresponds to the index values of an
|
||||
// MCDC Region's bitmap (see findExecutedTestVectors()).
|
||||
unsigned Index = 0;
|
||||
for (auto Cond = std::rbegin(TV); Cond != std::rend(TV); ++Cond) {
|
||||
Index <<= 1;
|
||||
Index |= (*Cond == MCDCRecord::MCDC_True) ? 0x1 : 0x0;
|
||||
}
|
||||
|
||||
// Copy the completed test vector to the vector of testvectors.
|
||||
TestVectors[Index] = TV;
|
||||
|
||||
// The final value (T,F) is equal to the last non-dontcare state on the
|
||||
// path (in a short-circuiting system).
|
||||
TestVectors[Index].push_back(Result);
|
||||
}
|
||||
|
||||
void shouldCopyOffTestVectorForTruePath(MCDCRecord::TestVector &TV,
|
||||
unsigned ID) {
|
||||
// Branch regions are hashed based on an ID.
|
||||
const CounterMappingRegion *Branch = Map[ID];
|
||||
|
||||
TV[ID - 1] = MCDCRecord::MCDC_True;
|
||||
if (Branch->MCDCParams.TrueID > 0)
|
||||
buildTestVector(TV, Branch->MCDCParams.TrueID);
|
||||
else
|
||||
recordTestVector(TV, MCDCRecord::MCDC_True);
|
||||
}
|
||||
|
||||
void shouldCopyOffTestVectorForFalsePath(MCDCRecord::TestVector &TV,
|
||||
unsigned ID) {
|
||||
// Branch regions are hashed based on an ID.
|
||||
const CounterMappingRegion *Branch = Map[ID];
|
||||
|
||||
TV[ID - 1] = MCDCRecord::MCDC_False;
|
||||
if (Branch->MCDCParams.FalseID > 0)
|
||||
buildTestVector(TV, Branch->MCDCParams.FalseID);
|
||||
else
|
||||
recordTestVector(TV, MCDCRecord::MCDC_False);
|
||||
}
|
||||
|
||||
/// Starting with the base test vector, build a comprehensive list of
|
||||
/// possible test vectors by recursively walking the branch condition IDs
|
||||
/// provided. Once an end node is reached, record the test vector in a vector
|
||||
/// of test vectors that can be matched against during MC/DC analysis, and
|
||||
/// then reset the positions to 'DontCare'.
|
||||
void buildTestVector(MCDCRecord::TestVector &TV, unsigned ID = 1) {
|
||||
shouldCopyOffTestVectorForTruePath(TV, ID);
|
||||
shouldCopyOffTestVectorForFalsePath(TV, ID);
|
||||
|
||||
// Reset back to DontCare.
|
||||
TV[ID - 1] = MCDCRecord::MCDC_DontCare;
|
||||
}
|
||||
|
||||
/// Walk the bits in the bitmap. A bit set to '1' indicates that the test
|
||||
/// vector at the corresponding index was executed during a test run.
|
||||
void findExecutedTestVectors(BitVector &ExecutedTestVectorBitmap) {
|
||||
for (unsigned Idx = 0; Idx < ExecutedTestVectorBitmap.size(); ++Idx) {
|
||||
if (ExecutedTestVectorBitmap[Idx] == 0)
|
||||
continue;
|
||||
assert(!TestVectors[Idx].empty() && "Test Vector doesn't exist.");
|
||||
ExecVectors.push_back(TestVectors[Idx]);
|
||||
}
|
||||
}
|
||||
|
||||
/// For a given condition and two executed Test Vectors, A and B, see if the
|
||||
/// two test vectors match forming an Independence Pair for the condition.
|
||||
/// For two test vectors to match, the following must be satisfied:
|
||||
/// - The condition's value in each test vector must be opposite.
|
||||
/// - The result's value in each test vector must be opposite.
|
||||
/// - All other conditions' values must be equal or marked as "don't care".
|
||||
bool matchTestVectors(unsigned Aidx, unsigned Bidx, unsigned ConditionIdx) {
|
||||
const MCDCRecord::TestVector &A = ExecVectors[Aidx];
|
||||
const MCDCRecord::TestVector &B = ExecVectors[Bidx];
|
||||
|
||||
// If condition values in both A and B aren't opposites, no match.
|
||||
// Because a value can be 0 (false), 1 (true), or -1 (DontCare), a check
|
||||
// that "XOR != 1" will ensure that the values are opposites and that
|
||||
// neither of them is a DontCare.
|
||||
// 1 XOR 0 == 1 | 0 XOR 0 == 0 | -1 XOR 0 == -1
|
||||
// 1 XOR 1 == 0 | 0 XOR 1 == 1 | -1 XOR 1 == -2
|
||||
// 1 XOR -1 == -2 | 0 XOR -1 == -1 | -1 XOR -1 == 0
|
||||
if ((A[ConditionIdx] ^ B[ConditionIdx]) != 1)
|
||||
return false;
|
||||
|
||||
// If the results of both A and B aren't opposites, no match.
|
||||
if ((A[NumConditions] ^ B[NumConditions]) != 1)
|
||||
return false;
|
||||
|
||||
for (unsigned Idx = 0; Idx < NumConditions; ++Idx) {
|
||||
// Look for other conditions that don't match. Skip over the given
|
||||
// Condition as well as any conditions marked as "don't care".
|
||||
const auto ARecordTyForCond = A[Idx];
|
||||
const auto BRecordTyForCond = B[Idx];
|
||||
if (Idx == ConditionIdx ||
|
||||
ARecordTyForCond == MCDCRecord::MCDC_DontCare ||
|
||||
BRecordTyForCond == MCDCRecord::MCDC_DontCare)
|
||||
continue;
|
||||
|
||||
// If there is a condition mismatch with any of the other conditions,
|
||||
// there is no match for the test vectors.
|
||||
if (ARecordTyForCond != BRecordTyForCond)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, match.
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Find all possible Independence Pairs for a boolean expression given its
|
||||
/// executed Test Vectors. This process involves looking at each condition
|
||||
/// and attempting to find two Test Vectors that "match", giving us a pair.
|
||||
void findIndependencePairs() {
|
||||
unsigned NumTVs = ExecVectors.size();
|
||||
|
||||
// For each condition.
|
||||
for (unsigned C = 0; C < NumConditions; ++C) {
|
||||
bool PairFound = false;
|
||||
|
||||
// For each executed test vector.
|
||||
for (unsigned I = 0; !PairFound && I < NumTVs; ++I) {
|
||||
// Compared to every other executed test vector.
|
||||
for (unsigned J = 0; !PairFound && J < NumTVs; ++J) {
|
||||
if (I == J)
|
||||
continue;
|
||||
|
||||
// If a matching pair of vectors is found, record them.
|
||||
if ((PairFound = matchTestVectors(I, J, C)))
|
||||
IndependencePairs[C] = std::make_pair(I + 1, J + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/// Process the MC/DC Record in order to produce a result for a boolean
|
||||
/// expression. This process includes tracking the conditions that comprise
|
||||
/// the decision region, calculating the list of all possible test vectors,
|
||||
/// marking the executed test vectors, and then finding an Independence Pair
|
||||
/// out of the executed test vectors for each condition in the boolean
|
||||
/// expression. A condition is tracked to ensure that its ID can be mapped to
|
||||
/// its ordinal position in the boolean expression. The condition's source
|
||||
/// location is also tracked, as well as whether it is constant folded (in
|
||||
/// which case it is excuded from the metric).
|
||||
MCDCRecord processMCDCRecord() {
|
||||
unsigned I = 0;
|
||||
MCDCRecord::CondIDMap PosToID;
|
||||
MCDCRecord::LineColPairMap CondLoc;
|
||||
|
||||
// Walk the Record's BranchRegions (representing Conditions) in order to:
|
||||
// - Hash the condition based on its corresponding ID. This will be used to
|
||||
// calculate the test vectors.
|
||||
// - Keep a map of the condition's ordinal position (1, 2, 3, 4) to its
|
||||
// actual ID. This will be used to visualize the conditions in the
|
||||
// correct order.
|
||||
// - Keep track of the condition source location. This will be used to
|
||||
// visualize where the condition is.
|
||||
// - Record whether the condition is constant folded so that we exclude it
|
||||
// from being measured.
|
||||
for (const auto &B : Branches) {
|
||||
Map[B.MCDCParams.ID] = &B;
|
||||
PosToID[I] = B.MCDCParams.ID - 1;
|
||||
CondLoc[I] = B.startLoc();
|
||||
Folded[I++] = (B.Count.isZero() && B.FalseCount.isZero());
|
||||
}
|
||||
|
||||
// Initialize a base test vector as 'DontCare'.
|
||||
MCDCRecord::TestVector TV(NumConditions, MCDCRecord::MCDC_DontCare);
|
||||
|
||||
// Use the base test vector to build the list of all possible test vectors.
|
||||
buildTestVector(TV);
|
||||
|
||||
// Using Profile Bitmap from runtime, mark the executed test vectors.
|
||||
findExecutedTestVectors(ExecutedTestVectorBitmap);
|
||||
|
||||
// Compare executed test vectors against each other to find an independence
|
||||
// pairs for each condition. This processing takes the most time.
|
||||
findIndependencePairs();
|
||||
|
||||
// Record Test vectors, executed vectors, and independence pairs.
|
||||
MCDCRecord Res(Region, ExecVectors, IndependencePairs, Folded, PosToID,
|
||||
CondLoc);
|
||||
return Res;
|
||||
}
|
||||
};
|
||||
|
||||
Expected<MCDCRecord> CounterMappingContext::evaluateMCDCRegion(
|
||||
CounterMappingRegion Region, BitVector ExecutedTestVectorBitmap,
|
||||
ArrayRef<CounterMappingRegion> Branches) {
|
||||
|
||||
MCDCRecordProcessor MCDCProcessor(ExecutedTestVectorBitmap, Region, Branches);
|
||||
return MCDCProcessor.processMCDCRecord();
|
||||
}
|
||||
|
||||
unsigned CounterMappingContext::getMaxCounterID(const Counter &C) const {
|
||||
struct StackElem {
|
||||
Counter ICounter;
|
||||
@@ -303,6 +562,24 @@ static unsigned getMaxCounterID(const CounterMappingContext &Ctx,
|
||||
return MaxCounterID;
|
||||
}
|
||||
|
||||
static unsigned getMaxBitmapSize(const CounterMappingContext &Ctx,
|
||||
const CoverageMappingRecord &Record) {
|
||||
unsigned MaxBitmapID = 0;
|
||||
unsigned NumConditions = 0;
|
||||
// The last DecisionRegion has the highest bitmap byte index used in the
|
||||
// function, which when combined with its number of conditions, yields the
|
||||
// full bitmap size.
|
||||
for (const auto &Region : reverse(Record.MappingRegions)) {
|
||||
if (Region.Kind == CounterMappingRegion::MCDCDecisionRegion) {
|
||||
MaxBitmapID = Region.MCDCParams.BitmapIdx;
|
||||
NumConditions = Region.MCDCParams.NumConditions;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unsigned SizeInBits = llvm::alignTo(uint64_t(1) << NumConditions, CHAR_BIT);
|
||||
return MaxBitmapID + (SizeInBits / CHAR_BIT);
|
||||
}
|
||||
|
||||
Error CoverageMapping::loadFunctionRecord(
|
||||
const CoverageMappingRecord &Record,
|
||||
IndexedInstrProfReader &ProfileReader) {
|
||||
@@ -326,12 +603,28 @@ Error CoverageMapping::loadFunctionRecord(
|
||||
FuncHashMismatches.emplace_back(std::string(Record.FunctionName),
|
||||
Record.FunctionHash);
|
||||
return Error::success();
|
||||
} else if (IPE != instrprof_error::unknown_function)
|
||||
}
|
||||
if (IPE != instrprof_error::unknown_function)
|
||||
return make_error<InstrProfError>(IPE);
|
||||
Counts.assign(getMaxCounterID(Ctx, Record) + 1, 0);
|
||||
}
|
||||
Ctx.setCounts(Counts);
|
||||
|
||||
std::vector<uint8_t> BitmapBytes;
|
||||
if (Error E = ProfileReader.getFunctionBitmapBytes(
|
||||
Record.FunctionName, Record.FunctionHash, BitmapBytes)) {
|
||||
instrprof_error IPE = std::get<0>(InstrProfError::take(std::move(E)));
|
||||
if (IPE == instrprof_error::hash_mismatch) {
|
||||
FuncHashMismatches.emplace_back(std::string(Record.FunctionName),
|
||||
Record.FunctionHash);
|
||||
return Error::success();
|
||||
}
|
||||
if (IPE != instrprof_error::unknown_function)
|
||||
return make_error<InstrProfError>(IPE);
|
||||
BitmapBytes.assign(getMaxBitmapSize(Ctx, Record) + 1, 0);
|
||||
}
|
||||
Ctx.setBitmapBytes(BitmapBytes);
|
||||
|
||||
assert(!Record.MappingRegions.empty() && "Function has no regions");
|
||||
|
||||
// This coverage record is a zero region for a function that's unused in
|
||||
@@ -343,8 +636,20 @@ Error CoverageMapping::loadFunctionRecord(
|
||||
Record.MappingRegions[0].Count.isZero() && Counts[0] > 0)
|
||||
return Error::success();
|
||||
|
||||
unsigned NumConds = 0;
|
||||
const CounterMappingRegion *MCDCDecision;
|
||||
std::vector<CounterMappingRegion> MCDCBranches;
|
||||
|
||||
FunctionRecord Function(OrigFuncName, Record.Filenames);
|
||||
for (const auto &Region : Record.MappingRegions) {
|
||||
// If an MCDCDecisionRegion is seen, track the BranchRegions that follow
|
||||
// it according to Region.NumConditions.
|
||||
if (Region.Kind == CounterMappingRegion::MCDCDecisionRegion) {
|
||||
assert(NumConds == 0);
|
||||
MCDCDecision = &Region;
|
||||
NumConds = Region.MCDCParams.NumConditions;
|
||||
continue;
|
||||
}
|
||||
Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
|
||||
if (auto E = ExecutionCount.takeError()) {
|
||||
consumeError(std::move(E));
|
||||
@@ -356,6 +661,44 @@ Error CoverageMapping::loadFunctionRecord(
|
||||
return Error::success();
|
||||
}
|
||||
Function.pushRegion(Region, *ExecutionCount, *AltExecutionCount);
|
||||
|
||||
// If a MCDCDecisionRegion was seen, store the BranchRegions that
|
||||
// correspond to it in a vector, according to the number of conditions
|
||||
// recorded for the region (tracked by NumConds).
|
||||
if (NumConds > 0 && Region.Kind == CounterMappingRegion::MCDCBranchRegion) {
|
||||
MCDCBranches.push_back(Region);
|
||||
|
||||
// As we move through all of the MCDCBranchRegions that follow the
|
||||
// MCDCDecisionRegion, decrement NumConds to make sure we account for
|
||||
// them all before we calculate the bitmap of executed test vectors.
|
||||
if (--NumConds == 0) {
|
||||
// Evaluating the test vector bitmap for the decision region entails
|
||||
// calculating precisely what bits are pertinent to this region alone.
|
||||
// This is calculated based on the recorded offset into the global
|
||||
// profile bitmap; the length is calculated based on the recorded
|
||||
// number of conditions.
|
||||
Expected<BitVector> ExecutedTestVectorBitmap =
|
||||
Ctx.evaluateBitmap(MCDCDecision);
|
||||
if (auto E = ExecutedTestVectorBitmap.takeError()) {
|
||||
consumeError(std::move(E));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
// Since the bitmap identifies the executed test vectors for an MC/DC
|
||||
// DecisionRegion, all of the information is now available to process.
|
||||
// This is where the bulk of the MC/DC progressing takes place.
|
||||
Expected<MCDCRecord> Record = Ctx.evaluateMCDCRegion(
|
||||
*MCDCDecision, *ExecutedTestVectorBitmap, MCDCBranches);
|
||||
if (auto E = Record.takeError()) {
|
||||
consumeError(std::move(E));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
// Save the MC/DC Record so that it can be visualized later.
|
||||
Function.pushMCDCRecord(*Record);
|
||||
MCDCBranches.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't create records for (filenames, function) pairs we've already seen.
|
||||
@@ -862,6 +1205,10 @@ CoverageData CoverageMapping::getCoverageForFile(StringRef Filename) const {
|
||||
for (const auto &CR : Function.CountedBranchRegions)
|
||||
if (FileIDs.test(CR.FileID) && (CR.FileID == CR.ExpandedFileID))
|
||||
FileCoverage.BranchRegions.push_back(CR);
|
||||
// Capture MCDC records specific to the function.
|
||||
for (const auto &MR : Function.MCDCRecords)
|
||||
if (FileIDs.test(MR.getDecisionRegion().FileID))
|
||||
FileCoverage.MCDCRecords.push_back(MR);
|
||||
}
|
||||
|
||||
LLVM_DEBUG(dbgs() << "Emitting segments for file: " << Filename << "\n");
|
||||
@@ -914,6 +1261,11 @@ CoverageMapping::getCoverageForFunction(const FunctionRecord &Function) const {
|
||||
if (CR.FileID == *MainFileID)
|
||||
FunctionCoverage.BranchRegions.push_back(CR);
|
||||
|
||||
// Capture MCDC records specific to the function.
|
||||
for (const auto &MR : Function.MCDCRecords)
|
||||
if (MR.getDecisionRegion().FileID == *MainFileID)
|
||||
FunctionCoverage.MCDCRecords.push_back(MR);
|
||||
|
||||
LLVM_DEBUG(dbgs() << "Emitting segments for function: " << Function.Name
|
||||
<< "\n");
|
||||
FunctionCoverage.Segments = SegmentBuilder::buildSegments(Regions);
|
||||
|
||||
@@ -244,6 +244,7 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
|
||||
unsigned LineStart = 0;
|
||||
for (size_t I = 0; I < NumRegions; ++I) {
|
||||
Counter C, C2;
|
||||
uint64_t BIDX = 0, NC = 0, ID = 0, TID = 0, FID = 0;
|
||||
CounterMappingRegion::RegionKind Kind = CounterMappingRegion::CodeRegion;
|
||||
|
||||
// Read the combined counter + region kind.
|
||||
@@ -294,6 +295,27 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
|
||||
if (auto Err = readCounter(C2))
|
||||
return Err;
|
||||
break;
|
||||
case CounterMappingRegion::MCDCBranchRegion:
|
||||
// For a MCDC Branch Region, read two successive counters and 3 IDs.
|
||||
Kind = CounterMappingRegion::MCDCBranchRegion;
|
||||
if (auto Err = readCounter(C))
|
||||
return Err;
|
||||
if (auto Err = readCounter(C2))
|
||||
return Err;
|
||||
if (auto Err = readIntMax(ID, std::numeric_limits<unsigned>::max()))
|
||||
return Err;
|
||||
if (auto Err = readIntMax(TID, std::numeric_limits<unsigned>::max()))
|
||||
return Err;
|
||||
if (auto Err = readIntMax(FID, std::numeric_limits<unsigned>::max()))
|
||||
return Err;
|
||||
break;
|
||||
case CounterMappingRegion::MCDCDecisionRegion:
|
||||
Kind = CounterMappingRegion::MCDCDecisionRegion;
|
||||
if (auto Err = readIntMax(BIDX, std::numeric_limits<unsigned>::max()))
|
||||
return Err;
|
||||
if (auto Err = readIntMax(NC, std::numeric_limits<unsigned>::max()))
|
||||
return Err;
|
||||
break;
|
||||
default:
|
||||
return make_error<CoverageMapError>(coveragemap_error::malformed,
|
||||
"region kind is incorrect");
|
||||
@@ -347,9 +369,14 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
|
||||
dbgs() << "\n";
|
||||
});
|
||||
|
||||
auto CMR = CounterMappingRegion(C, C2, InferredFileID, ExpandedFileID,
|
||||
LineStart, ColumnStart,
|
||||
LineStart + NumLines, ColumnEnd, Kind);
|
||||
auto CMR = CounterMappingRegion(
|
||||
C, C2,
|
||||
CounterMappingRegion::MCDCParameters{
|
||||
static_cast<unsigned>(BIDX), static_cast<unsigned>(NC),
|
||||
static_cast<unsigned>(ID), static_cast<unsigned>(TID),
|
||||
static_cast<unsigned>(FID)},
|
||||
InferredFileID, ExpandedFileID, LineStart, ColumnStart,
|
||||
LineStart + NumLines, ColumnEnd, Kind);
|
||||
if (CMR.startLoc() > CMR.endLoc())
|
||||
return make_error<CoverageMapError>(
|
||||
coveragemap_error::malformed,
|
||||
|
||||
@@ -237,6 +237,23 @@ void CoverageMappingWriter::write(raw_ostream &OS) {
|
||||
writeCounter(MinExpressions, Count, OS);
|
||||
writeCounter(MinExpressions, FalseCount, OS);
|
||||
break;
|
||||
case CounterMappingRegion::MCDCBranchRegion:
|
||||
encodeULEB128(unsigned(I->Kind)
|
||||
<< Counter::EncodingCounterTagAndExpansionRegionTagBits,
|
||||
OS);
|
||||
writeCounter(MinExpressions, Count, OS);
|
||||
writeCounter(MinExpressions, FalseCount, OS);
|
||||
encodeULEB128(unsigned(I->MCDCParams.ID), OS);
|
||||
encodeULEB128(unsigned(I->MCDCParams.TrueID), OS);
|
||||
encodeULEB128(unsigned(I->MCDCParams.FalseID), OS);
|
||||
break;
|
||||
case CounterMappingRegion::MCDCDecisionRegion:
|
||||
encodeULEB128(unsigned(I->Kind)
|
||||
<< Counter::EncodingCounterTagAndExpansionRegionTagBits,
|
||||
OS);
|
||||
encodeULEB128(unsigned(I->MCDCParams.BitmapIdx), OS);
|
||||
encodeULEB128(unsigned(I->MCDCParams.NumConditions), OS);
|
||||
break;
|
||||
}
|
||||
assert(I->LineStart >= PrevLineStart);
|
||||
encodeULEB128(I->LineStart - PrevLineStart, OS);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
CHECK: {"data":
|
||||
CHECK-SAME: [{
|
||||
CHECK: {
|
||||
"data": CHECK - SAME: [
|
||||
{
|
||||
CHECK-SAME: "files":[
|
||||
CHECK-SAME: {"branches":[],
|
||||
CHECK-SAME: "expansions":[],
|
||||
CHECK-SAME: "filename":"/tmp/binary-formats.c",
|
||||
CHECK-SAME: "mcdc_records":[],
|
||||
CHECK-SAME: "segments":
|
||||
CHECK-SAME: 4,40,100,true,true,false
|
||||
CHECK-SAME: 4,42,0,false,false,false
|
||||
@@ -11,11 +13,13 @@ CHECK-SAME: "summary":{"branches":{"count":0,"covered":0,"notcovered":0,"per
|
||||
CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
|
||||
CHECK-SAME: "instantiations":{"count":1,"covered":1,"percent":100},
|
||||
CHECK-SAME: "lines":{"count":1,"covered":1,"percent":100},
|
||||
CHECK-SAME: "mcdc":{"count":0,"covered":0,"notcovered":0,"percent":0},
|
||||
CHECK-SAME: "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}}}
|
||||
CHECK-SAME: ],
|
||||
CHECK-SAME: "functions":[
|
||||
CHECK-SAME: {"branches":[],
|
||||
CHECK-SAME: "count":100,"filenames":["/tmp/binary-formats.c"],"name":"main",
|
||||
CHECK-SAME: "count":100,"filenames":["/tmp/binary-formats.c"],
|
||||
CHECK-SAME: "mcdc_records":[],"name":"main",
|
||||
CHECK-SAME: "regions":
|
||||
CHECK-SAME: 4,40,4,42,100,0,0,0
|
||||
CHECK-SAME: }
|
||||
@@ -25,6 +29,7 @@ CHECK-SAME: {"branches":{"count":0,"covered":0,"notcovered":0,"percent":0},
|
||||
CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
|
||||
CHECK-SAME: "instantiations":{"count":1,"covered":1,"percent":100},
|
||||
CHECK-SAME: "lines":{"count":1,"covered":1,"percent":100},
|
||||
CHECk-SAME: "mcdc":{"count":0,"covered":0,"notcovered":0,"percent":0},
|
||||
CHECK-SAME: "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}}}
|
||||
CHECK-SAME: ],
|
||||
CHECK-SAME: "type":"llvm.coverage.json.export"
|
||||
|
||||
117
llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.cpp
Normal file
117
llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
bool case0(bool a) {
|
||||
return 0 && a;
|
||||
}
|
||||
bool case1(bool a) {
|
||||
return a && 0;
|
||||
}
|
||||
bool case2(bool a) {
|
||||
return 1 && a;
|
||||
}
|
||||
bool case3(bool a) {
|
||||
return a && 1;
|
||||
}
|
||||
bool case4(bool a) {
|
||||
return 1 || a;
|
||||
}
|
||||
bool case5(bool a) {
|
||||
return a || 1;
|
||||
}
|
||||
bool case6(bool a) {
|
||||
return 0 || a;
|
||||
}
|
||||
bool case7(bool a) {
|
||||
return a || 0;
|
||||
}
|
||||
|
||||
bool case8(bool a, bool b) {
|
||||
return 0 && a && b;
|
||||
}
|
||||
bool case9(bool a, bool b) {
|
||||
return a && 0 && b;
|
||||
}
|
||||
bool casea(bool a, bool b) {
|
||||
return 1 && a && b;
|
||||
}
|
||||
bool caseb(bool a, bool b) {
|
||||
return a && 1 && b;
|
||||
}
|
||||
bool casec(bool a, bool b) {
|
||||
return 1 || a || b;
|
||||
}
|
||||
bool cased(bool a, bool b) {
|
||||
return a || 1 || b;
|
||||
}
|
||||
bool casee(bool a, bool b) {
|
||||
return 0 || a || b;
|
||||
}
|
||||
bool casef(bool a, bool b) {
|
||||
return a || 0 || b;
|
||||
}
|
||||
|
||||
bool caseg(bool a, bool b) {
|
||||
return b && a && 0;
|
||||
}
|
||||
bool caseh(bool a, bool b) {
|
||||
return b && 0 && a;
|
||||
}
|
||||
bool casei(bool a, bool b) {
|
||||
return b && a && 1;
|
||||
}
|
||||
bool casej(bool a, bool b) {
|
||||
return b && 1 && a;
|
||||
}
|
||||
bool casek(bool a, bool b) {
|
||||
return b || a || 1;
|
||||
}
|
||||
bool casel(bool a, bool b) {
|
||||
return b || 1 || a;
|
||||
}
|
||||
bool casem(bool a, bool b) {
|
||||
return b || a || 0;
|
||||
}
|
||||
bool casen(bool a, bool b) {
|
||||
return b || 0 || a;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
extern void __llvm_profile_write_file(void);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
bool a = atoi(argv[1]);
|
||||
bool b = atoi(argv[2]);
|
||||
volatile bool c;
|
||||
|
||||
c = case0(a);
|
||||
c = case1(a);
|
||||
c = case2(a);
|
||||
c = case3(a);
|
||||
c = case4(a);
|
||||
c = case5(a);
|
||||
c = case6(a);
|
||||
c = case7(a);
|
||||
|
||||
c = case8(a, b);
|
||||
c = case9(a, b);
|
||||
c = casea(a, b);
|
||||
c = caseb(a, b);
|
||||
c = casec(a, b);
|
||||
c = cased(a, b);
|
||||
c = casee(a, b);
|
||||
c = casef(a, b);
|
||||
|
||||
c = caseg(a, b);
|
||||
c = caseh(a, b);
|
||||
c = casei(a, b);
|
||||
c = casej(a, b);
|
||||
c = casek(a, b);
|
||||
c = casel(a, b);
|
||||
c = casem(a, b);
|
||||
c = casen(a, b);
|
||||
|
||||
__llvm_profile_write_file();
|
||||
return 0;
|
||||
}
|
||||
BIN
llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.o
Executable file
BIN
llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.o
Executable file
Binary file not shown.
398
llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.proftext
Normal file
398
llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.proftext
Normal file
@@ -0,0 +1,398 @@
|
||||
_Z5case8bb
|
||||
# Func Hash:
|
||||
99214
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x1
|
||||
|
||||
|
||||
_Z5case5b
|
||||
# Func Hash:
|
||||
1551
|
||||
# Num Counters:
|
||||
3
|
||||
# Counter Values:
|
||||
4
|
||||
1
|
||||
0
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x6
|
||||
|
||||
|
||||
_Z5caseabb
|
||||
# Func Hash:
|
||||
99214
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
3
|
||||
2
|
||||
4
|
||||
3
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0xa2
|
||||
|
||||
|
||||
_Z5case6b
|
||||
# Func Hash:
|
||||
1551
|
||||
# Num Counters:
|
||||
3
|
||||
# Counter Values:
|
||||
4
|
||||
4
|
||||
1
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x5
|
||||
|
||||
|
||||
_Z5casegbb
|
||||
# Func Hash:
|
||||
99214
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
2
|
||||
0
|
||||
3
|
||||
2
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x23
|
||||
|
||||
|
||||
_Z5case1b
|
||||
# Func Hash:
|
||||
1550
|
||||
# Num Counters:
|
||||
3
|
||||
# Counter Values:
|
||||
4
|
||||
3
|
||||
0
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x3
|
||||
|
||||
|
||||
_Z5case7b
|
||||
# Func Hash:
|
||||
1551
|
||||
# Num Counters:
|
||||
3
|
||||
# Counter Values:
|
||||
4
|
||||
1
|
||||
1
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x3
|
||||
|
||||
|
||||
_Z5casedbb
|
||||
# Func Hash:
|
||||
99279
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
0
|
||||
0
|
||||
1
|
||||
0
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x12
|
||||
|
||||
|
||||
_Z5casekbb
|
||||
# Func Hash:
|
||||
99279
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
0
|
||||
0
|
||||
1
|
||||
0
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x12
|
||||
|
||||
|
||||
_Z5casehbb
|
||||
# Func Hash:
|
||||
99214
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
0
|
||||
0
|
||||
3
|
||||
0
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x3
|
||||
|
||||
|
||||
_Z5case4b
|
||||
# Func Hash:
|
||||
1551
|
||||
# Num Counters:
|
||||
3
|
||||
# Counter Values:
|
||||
4
|
||||
0
|
||||
0
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x2
|
||||
|
||||
|
||||
_Z5caseibb
|
||||
# Func Hash:
|
||||
99214
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
2
|
||||
2
|
||||
3
|
||||
2
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x83
|
||||
|
||||
|
||||
_Z5case2b
|
||||
# Func Hash:
|
||||
1550
|
||||
# Num Counters:
|
||||
3
|
||||
# Counter Values:
|
||||
4
|
||||
4
|
||||
3
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0xa
|
||||
|
||||
|
||||
_Z5casefbb
|
||||
# Func Hash:
|
||||
99279
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
1
|
||||
0
|
||||
1
|
||||
1
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x6
|
||||
|
||||
|
||||
_Z5caselbb
|
||||
# Func Hash:
|
||||
99279
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
0
|
||||
0
|
||||
1
|
||||
0
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x12
|
||||
|
||||
|
||||
_Z5casenbb
|
||||
# Func Hash:
|
||||
99279
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
1
|
||||
0
|
||||
1
|
||||
1
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x6
|
||||
|
||||
|
||||
_Z5case9bb
|
||||
# Func Hash:
|
||||
99214
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
0
|
||||
0
|
||||
3
|
||||
0
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x3
|
||||
|
||||
|
||||
_Z5casecbb
|
||||
# Func Hash:
|
||||
99279
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x2
|
||||
|
||||
|
||||
_Z5casebbb
|
||||
# Func Hash:
|
||||
99214
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
3
|
||||
2
|
||||
3
|
||||
3
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0xa1
|
||||
|
||||
|
||||
_Z5case0b
|
||||
# Func Hash:
|
||||
1550
|
||||
# Num Counters:
|
||||
3
|
||||
# Counter Values:
|
||||
4
|
||||
0
|
||||
0
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x1
|
||||
|
||||
|
||||
_Z5casejbb
|
||||
# Func Hash:
|
||||
99214
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
3
|
||||
2
|
||||
3
|
||||
3
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0xa1
|
||||
|
||||
|
||||
_Z5caseebb
|
||||
# Func Hash:
|
||||
99279
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
1
|
||||
0
|
||||
4
|
||||
1
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x14
|
||||
|
||||
|
||||
main
|
||||
# Func Hash:
|
||||
24
|
||||
# Num Counters:
|
||||
1
|
||||
# Counter Values:
|
||||
4
|
||||
|
||||
_Z5case3b
|
||||
# Func Hash:
|
||||
1550
|
||||
# Num Counters:
|
||||
3
|
||||
# Counter Values:
|
||||
4
|
||||
3
|
||||
3
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x9
|
||||
|
||||
|
||||
_Z5casembb
|
||||
# Func Hash:
|
||||
99279
|
||||
# Num Counters:
|
||||
5
|
||||
# Counter Values:
|
||||
4
|
||||
0
|
||||
0
|
||||
1
|
||||
0
|
||||
# Num Bitmask Bytes:
|
||||
$1
|
||||
# Bitmask Byte Values:
|
||||
0x12
|
||||
27
llvm/test/tools/llvm-cov/Inputs/mcdc-const.cpp
Normal file
27
llvm/test/tools/llvm-cov/Inputs/mcdc-const.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" {
|
||||
extern void __llvm_profile_write_file(void);
|
||||
}
|
||||
|
||||
extern int foo();
|
||||
|
||||
void test(bool a, bool b, bool c, bool d) {
|
||||
|
||||
if ((a && 1) || (0 && d) || 0)
|
||||
printf("test1 decision true\n");
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test(true,false,true,false);
|
||||
test(true,false,true,true);
|
||||
test(true,true,false,false);
|
||||
test(false,true,true,false);
|
||||
|
||||
test(true,false,false,false);
|
||||
|
||||
__llvm_profile_write_file();
|
||||
return 0;
|
||||
}
|
||||
BIN
llvm/test/tools/llvm-cov/Inputs/mcdc-const.o
Executable file
BIN
llvm/test/tools/llvm-cov/Inputs/mcdc-const.o
Executable file
Binary file not shown.
30
llvm/test/tools/llvm-cov/Inputs/mcdc-const.proftext
Normal file
30
llvm/test/tools/llvm-cov/Inputs/mcdc-const.proftext
Normal file
@@ -0,0 +1,30 @@
|
||||
main
|
||||
# Func Hash:
|
||||
24
|
||||
# Num Counters:
|
||||
1
|
||||
# Counter Values:
|
||||
1
|
||||
|
||||
_Z4testbbbb
|
||||
# Func Hash:
|
||||
703556281489
|
||||
# Num Counters:
|
||||
9
|
||||
# Counter Values:
|
||||
5
|
||||
4
|
||||
1
|
||||
1
|
||||
1
|
||||
4
|
||||
4
|
||||
0
|
||||
0
|
||||
# Num Bitmask Bytes:
|
||||
$4
|
||||
# Bitmask Byte Values:
|
||||
1
|
||||
2
|
||||
0
|
||||
0
|
||||
42
llvm/test/tools/llvm-cov/Inputs/mcdc-general-none.proftext
Normal file
42
llvm/test/tools/llvm-cov/Inputs/mcdc-general-none.proftext
Normal file
@@ -0,0 +1,42 @@
|
||||
main
|
||||
# Func Hash:
|
||||
24
|
||||
# Num Counters:
|
||||
1
|
||||
# Counter Values:
|
||||
1
|
||||
|
||||
_Z4testbbbb
|
||||
# Func Hash:
|
||||
9819241276358969079
|
||||
# Num Counters:
|
||||
19
|
||||
# Counter Values:
|
||||
7
|
||||
3
|
||||
5
|
||||
5
|
||||
2
|
||||
3
|
||||
1
|
||||
2
|
||||
3
|
||||
2
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
2
|
||||
4
|
||||
2
|
||||
2
|
||||
1
|
||||
# Num Bitmask Bytes:
|
||||
$6
|
||||
# Bitmask Byte Values:
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
36
llvm/test/tools/llvm-cov/Inputs/mcdc-general.cpp
Normal file
36
llvm/test/tools/llvm-cov/Inputs/mcdc-general.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" {
|
||||
extern void __llvm_profile_write_file(void);
|
||||
}
|
||||
|
||||
extern int foo();
|
||||
|
||||
void test(bool a, bool b, bool c, bool d) {
|
||||
|
||||
if ((a && b) || (c && d))
|
||||
printf("test1 decision true\n");
|
||||
|
||||
if (b && c) if (a && d)
|
||||
printf("test2 decision true\n");
|
||||
|
||||
if ((c && d) &&
|
||||
(a && b))
|
||||
printf("test3 decision true\n");
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test(false,false,false,false);
|
||||
test(true,false,true,false);
|
||||
test(true,false,true,true);
|
||||
test(true,true,false,false);
|
||||
|
||||
test(true,false,false,false);
|
||||
test(true,true,true,true);
|
||||
test(false,true,true,false);
|
||||
|
||||
__llvm_profile_write_file();
|
||||
return 0;
|
||||
}
|
||||
BIN
llvm/test/tools/llvm-cov/Inputs/mcdc-general.o
Executable file
BIN
llvm/test/tools/llvm-cov/Inputs/mcdc-general.o
Executable file
Binary file not shown.
42
llvm/test/tools/llvm-cov/Inputs/mcdc-general.proftext
Normal file
42
llvm/test/tools/llvm-cov/Inputs/mcdc-general.proftext
Normal file
@@ -0,0 +1,42 @@
|
||||
main
|
||||
# Func Hash:
|
||||
24
|
||||
# Num Counters:
|
||||
1
|
||||
# Counter Values:
|
||||
1
|
||||
|
||||
_Z4testbbbb
|
||||
# Func Hash:
|
||||
9819241276358969079
|
||||
# Num Counters:
|
||||
19
|
||||
# Counter Values:
|
||||
7
|
||||
3
|
||||
5
|
||||
5
|
||||
2
|
||||
3
|
||||
1
|
||||
2
|
||||
3
|
||||
2
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
2
|
||||
4
|
||||
2
|
||||
2
|
||||
1
|
||||
# Num Bitmask Bytes:
|
||||
$6
|
||||
# Bitmask Byte Values:
|
||||
0x2f
|
||||
0x8
|
||||
0xb
|
||||
0x9
|
||||
0x83
|
||||
0x80
|
||||
218
llvm/test/tools/llvm-cov/mcdc-const.test
Normal file
218
llvm/test/tools/llvm-cov/mcdc-const.test
Normal file
@@ -0,0 +1,218 @@
|
||||
// Test visualization of MC/DC constructs for constant-folded condition masking.
|
||||
|
||||
// RUN: llvm-profdata merge %S/Inputs/mcdc-const.proftext -o %t.profdata
|
||||
// RUN: llvm-cov show --show-branches=count --show-mcdc %S/Inputs/mcdc-const.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-const.cpp | FileCheck %s -check-prefix=CHECKGENERALCASE
|
||||
|
||||
// CHECKGENERALCASE: ------------------
|
||||
// CHECKGENERALCASE-NEXT: | Branch (12:8): [True: 4, False: 1]
|
||||
// CHECKGENERALCASE-NEXT: | Branch (12:13): [Folded - Ignored]
|
||||
// CHECKGENERALCASE-NEXT: | Branch (12:20): [Folded - Ignored]
|
||||
// CHECKGENERALCASE-NEXT: | Branch (12:25): [True: 0, False: 0]
|
||||
// CHECKGENERALCASE-NEXT: | Branch (12:31): [Folded - Ignored]
|
||||
// CHECKGENERALCASE-NEXT: ------------------
|
||||
// CHECKGENERALCASE-NEXT: |---> MC/DC Decision Region (12:7) to (12:32)
|
||||
// CHECKGENERALCASE-NEXT: |
|
||||
// CHECKGENERALCASE-NEXT: | Number of Conditions: 5
|
||||
// CHECKGENERALCASE-NEXT: | Condition C1 --> (12:8)
|
||||
// CHECKGENERALCASE-NEXT: | Condition C2 --> (12:13)
|
||||
// CHECKGENERALCASE-NEXT: | Condition C3 --> (12:20)
|
||||
// CHECKGENERALCASE-NEXT: | Condition C4 --> (12:25)
|
||||
// CHECKGENERALCASE-NEXT: | Condition C5 --> (12:31)
|
||||
// CHECKGENERALCASE-NEXT: |
|
||||
// CHECKGENERALCASE-NEXT: | Executed MC/DC Test Vectors:
|
||||
// CHECKGENERALCASE-NEXT: |
|
||||
// CHECKGENERALCASE-NEXT: | C1, C2, C3, C4, C5 Result
|
||||
// CHECKGENERALCASE-NEXT: | 1 { F, C, C, -, C = F }
|
||||
// CHECKGENERALCASE-NEXT: | 2 { T, C, C, -, C = T }
|
||||
// CHECKGENERALCASE-NEXT: |
|
||||
// CHECKGENERALCASE-NEXT: | C1-Pair: covered: (1,2)
|
||||
// CHECKGENERALCASE-NEXT: | C2-Pair: constant folded
|
||||
// CHECKGENERALCASE-NEXT: | C3-Pair: constant folded
|
||||
// CHECKGENERALCASE-NEXT: | C4-Pair: not covered
|
||||
// CHECKGENERALCASE-NEXT: | C5-Pair: constant folded
|
||||
// CHECKGENERALCASE-NEXT: | MC/DC Coverage for Decision: 50.00%
|
||||
// CHECKGENERALCASE-NEXT: |
|
||||
// CHECKGENERALCASE-NEXT: ------------------
|
||||
|
||||
// RUN: llvm-profdata merge %S/Inputs/mcdc-const-folding.proftext -o %t.profdata
|
||||
// RUN: llvm-cov show --show-mcdc %S/Inputs/mcdc-const-folding.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-const-folding.cpp | FileCheck %s -check-prefix=CHECKFULLCASE
|
||||
// RUN: llvm-cov report --show-mcdc-summary %S/Inputs/mcdc-const-folding.o -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-const-folding.cpp | FileCheck %s -check-prefix=REPORT
|
||||
|
||||
// CHECKFULLCASE: | 1 { C, - = F }
|
||||
// CHECKFULLCASE: | C1-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: not covered
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { F, C = F }
|
||||
// CHECKFULLCASE-NEXT: | 2 { T, C = F }
|
||||
// CHECKFULLCASE: | C1-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: constant folded
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { C, F = F }
|
||||
// CHECKFULLCASE-NEXT: | 2 { C, T = T }
|
||||
// CHECKFULLCASE: | C1-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: covered: (1,2)
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
|
||||
// CHECKFULLCASE: | 1 { F, C = F }
|
||||
// CHECKFULLCASE-NEXT: | 2 { T, C = T }
|
||||
// CHECKFULLCASE: | C1-Pair: covered: (1,2)
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: constant folded
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
|
||||
// CHECKFULLCASE: | 1 { C, - = T }
|
||||
// CHECKFULLCASE: | C1-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: not covered
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { T, C = T }
|
||||
// CHECKFULLCASE-NEXT: | 2 { F, C = T }
|
||||
// CHECKFULLCASE: | C1-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: constant folded
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { C, F = F }
|
||||
// CHECKFULLCASE-NEXT: | 2 { C, T = T }
|
||||
// CHECKFULLCASE: | C1-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: covered: (1,2)
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
|
||||
// CHECKFULLCASE: | 1 { F, C = F }
|
||||
// CHECKFULLCASE-NEXT: | 2 { T, C = T }
|
||||
// CHECKFULLCASE: | C1-Pair: covered: (1,2)
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: constant folded
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
|
||||
// CHECKFULLCASE: | 1 { C, -, - = F }
|
||||
// CHECKFULLCASE: | C1-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: not covered
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { F, C, - = F }
|
||||
// CHECKFULLCASE-NEXT: | 2 { T, C, - = F }
|
||||
// CHECKFULLCASE: | C1-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: not covered
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { C, F, - = F }
|
||||
// CHECKFULLCASE-NEXT: | 2 { C, T, F = F }
|
||||
// CHECKFULLCASE-NEXT: | 3 { C, T, T = T }
|
||||
// CHECKFULLCASE: | C1-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: covered: (1,3)
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: covered: (2,3)
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
|
||||
// CHECKFULLCASE: | 1 { F, C, - = F }
|
||||
// CHECKFULLCASE-NEXT: | 2 { T, C, F = F }
|
||||
// CHECKFULLCASE-NEXT: | 3 { T, C, T = T }
|
||||
// CHECKFULLCASE: | C1-Pair: covered: (1,3)
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: covered: (2,3)
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
|
||||
// CHECKFULLCASE: | 1 { C, -, - = T }
|
||||
// CHECKFULLCASE: | C1-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: not covered
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { T, C, - = T }
|
||||
// CHECKFULLCASE-NEXT: | 2 { F, C, - = T }
|
||||
// CHECKFULLCASE: | C1-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: not covered
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { C, F, T = T }
|
||||
// CHECKFULLCASE-NEXT: | 2 { C, T, - = T }
|
||||
// CHECKFULLCASE: | C1-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: not covered
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { T, C, - = T }
|
||||
// CHECKFULLCASE-NEXT: | 2 { F, C, T = T }
|
||||
// CHECKFULLCASE: | C1-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: not covered
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { F, -, C = F }
|
||||
// CHECKFULLCASE-NEXT: | 2 { T, F, C = F }
|
||||
// CHECKFULLCASE-NEXT: | 3 { T, T, C = F }
|
||||
// CHECKFULLCASE: | C1-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: constant folded
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { F, C, - = F }
|
||||
// CHECKFULLCASE-NEXT: | 2 { T, C, - = F }
|
||||
// CHECKFULLCASE: | C1-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: not covered
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { F, -, C = F }
|
||||
// CHECKFULLCASE-NEXT: | 2 { T, F, C = F }
|
||||
// CHECKFULLCASE-NEXT: | 3 { T, T, C = T }
|
||||
// CHECKFULLCASE: | C1-Pair: covered: (1,3)
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: covered: (2,3)
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: constant folded
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
|
||||
// CHECKFULLCASE: | 1 { F, C, - = F }
|
||||
// CHECKFULLCASE-NEXT: | 2 { T, C, F = F }
|
||||
// CHECKFULLCASE-NEXT: | 3 { T, C, T = T }
|
||||
// CHECKFULLCASE: | C1-Pair: covered: (1,3)
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: covered: (2,3)
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
|
||||
// CHECKFULLCASE: | 1 { T, -, C = T }
|
||||
// CHECKFULLCASE-NEXT: | 2 { F, T, C = T }
|
||||
// CHECKFULLCASE: | C1-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: constant folded
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { T, C, - = T }
|
||||
// CHECKFULLCASE-NEXT: | 2 { F, C, - = T }
|
||||
// CHECKFULLCASE: | C1-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: not covered
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { T, -, C = T }
|
||||
// CHECKFULLCASE-NEXT: | 2 { F, T, C = T }
|
||||
// CHECKFULLCASE: | C1-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: constant folded
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECKFULLCASE: | 1 { T, C, - = T }
|
||||
// CHECKFULLCASE-NEXT: | 2 { F, C, T = T }
|
||||
// CHECKFULLCASE: | C1-Pair: not covered
|
||||
// CHECKFULLCASE-NEXT: | C2-Pair: constant folded
|
||||
// CHECKFULLCASE-NEXT: | C3-Pair: not covered
|
||||
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
|
||||
|
||||
// REPORT: _Z5case0b {{.*}} 1 1 0.00%
|
||||
// REPORT-NEXT: _Z5case1b {{.*}} 1 1 0.00%
|
||||
// REPORT-NEXT: _Z5case2b {{.*}} 1 0 100.00%
|
||||
// REPORT-NEXT: _Z5case3b {{.*}} 1 0 100.00%
|
||||
// REPORT-NEXT: _Z5case4b {{.*}} 1 1 0.00%
|
||||
// REPORT-NEXT: _Z5case5b {{.*}} 1 1 0.00%
|
||||
// REPORT-NEXT: _Z5case6b {{.*}} 1 0 100.00%
|
||||
// REPORT-NEXT: _Z5case7b {{.*}} 1 0 100.00%
|
||||
// REPORT-NEXT: _Z5case8bb {{.*}} 2 2 0.00%
|
||||
// REPORT-NEXT: _Z5case9bb {{.*}} 2 2 0.00%
|
||||
// REPORT-NEXT: _Z5caseabb {{.*}} 2 0 100.00%
|
||||
// REPORT-NEXT: _Z5casebbb {{.*}} 2 0 100.00%
|
||||
// REPORT-NEXT: _Z5casecbb {{.*}} 2 2 0.00%
|
||||
// REPORT-NEXT: _Z5casedbb {{.*}} 2 2 0.00%
|
||||
// REPORT-NEXT: _Z5caseebb {{.*}} 2 2 0.00%
|
||||
// REPORT-NEXT: _Z5casefbb {{.*}} 2 2 0.00%
|
||||
// REPORT-NEXT: _Z5casegbb {{.*}} 2 2 0.00%
|
||||
// REPORT-NEXT: _Z5casehbb {{.*}} 2 2 0.00%
|
||||
// REPORT-NEXT: _Z5caseibb {{.*}} 2 0 100.00%
|
||||
// REPORT-NEXT: _Z5casejbb {{.*}} 2 0 100.00%
|
||||
// REPORT-NEXT: _Z5casekbb {{.*}} 2 2 0.00%
|
||||
// REPORT-NEXT: _Z5caselbb {{.*}} 2 2 0.00%
|
||||
// REPORT-NEXT: _Z5casembb {{.*}} 2 2 0.00%
|
||||
// REPORT-NEXT: _Z5casenbb {{.*}} 2 2 0.00%
|
||||
// REPORT: TOTAL {{.*}} 40 28 30.00%
|
||||
|
||||
Instructions for regenerating the test:
|
||||
|
||||
# cd %S/Inputs
|
||||
cp mcdc-const.cpp /tmp
|
||||
cp mcdc-const-folding.cpp /tmp
|
||||
|
||||
clang -fcoverage-mcdc -fprofile-instr-generate -fcoverage-compilation-dir=. \
|
||||
-fcoverage-mapping /tmp/mcdc-const.cpp -o /tmp/mcdc-const.o
|
||||
|
||||
clang -fcoverage-mcdc -fprofile-instr-generate -fcoverage-compilation-dir=. \
|
||||
-fcoverage-mapping /tmp/mcdc-const-folding.cpp -o /tmp/mcdc-const-folding.o
|
||||
|
||||
mv /tmp/mcdc-const.o %S/Inputs
|
||||
mv /tmp/mcdc-const-folding.o %S/Inputs
|
||||
18
llvm/test/tools/llvm-cov/mcdc-export-json.test
Normal file
18
llvm/test/tools/llvm-cov/mcdc-export-json.test
Normal file
@@ -0,0 +1,18 @@
|
||||
// RUN: llvm-profdata merge %S/Inputs/mcdc-general.proftext -o %t.profdata
|
||||
// RUN: llvm-cov export --format=text %S/Inputs/mcdc-general.o -instr-profile %t.profdata | FileCheck %s
|
||||
|
||||
// CHECK: 12,7,12,27,0,5,[true,true,true,true]
|
||||
// CHECK: 15,7,15,13,0,5,[true,true]
|
||||
// CHECK: 15,19,15,25,0,5,[true,false]
|
||||
// CHECK: 18,7,19,15,0,5,[true,true,false,true]
|
||||
// CHECK: "mcdc":{"count":12,"covered":10,"notcovered":2,"percent":83.333333333333343}
|
||||
|
||||
Instructions for regenerating the test:
|
||||
|
||||
# cd %S/Inputs
|
||||
cp mcdc-general.cpp /tmp
|
||||
|
||||
clang -fcoverage-mcdc -fprofile-instr-generate -fcoverage-compilation-dir=. \
|
||||
-fcoverage-mapping /tmp/mcdc-general.cpp -o /tmp/mcdc-const.o
|
||||
|
||||
mv /tmp/mcdc-general.o %S/Inputs
|
||||
82
llvm/test/tools/llvm-cov/mcdc-general-none.test
Normal file
82
llvm/test/tools/llvm-cov/mcdc-general-none.test
Normal file
@@ -0,0 +1,82 @@
|
||||
// Test visualization of general MC/DC constructs with 0 executed test vectors.
|
||||
|
||||
// RUN: llvm-profdata merge %S/Inputs/mcdc-general-none.proftext -o %t.profdata
|
||||
// RUN: llvm-cov show --show-mcdc %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s
|
||||
// RUN: llvm-cov report --show-mcdc-summary %S/Inputs/mcdc-general.o -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=REPORT
|
||||
|
||||
// CHECK: test(bool
|
||||
|
||||
// CHECK: ------------------
|
||||
// CHECK-NEXT: |---> MC/DC Decision Region (12:7) to (12:27)
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | Number of Conditions: 4
|
||||
// CHECK-NEXT: | Condition C1 --> (12:8)
|
||||
// CHECK-NEXT: | Condition C2 --> (12:13)
|
||||
// CHECK-NEXT: | Condition C3 --> (12:20)
|
||||
// CHECK-NEXT: | Condition C4 --> (12:25)
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | Executed MC/DC Test Vectors:
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | None.
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | C1-Pair: not covered
|
||||
// CHECK-NEXT: | C2-Pair: not covered
|
||||
// CHECK-NEXT: | C3-Pair: not covered
|
||||
// CHECK-NEXT: | C4-Pair: not covered
|
||||
// CHECK-NEXT: | MC/DC Coverage for Decision: 0.00%
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: ------------------
|
||||
|
||||
|
||||
// Turn off MC/DC visualization.
|
||||
// RUN: llvm-cov show %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=NOMCDC
|
||||
// NOMCDC-NOT: MC/DC Decision Region
|
||||
|
||||
// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover MC/DC Conditions Miss Cover
|
||||
// REPORT-NEXT: -------------------------------------------------------------------------------------------------------------------------------------------
|
||||
// REPORT-NEXT: _Z4testbbbb 25 0 100.00% 9 0 100.00% 24 2 91.67% 12 12 0.00%
|
||||
// REPORT-NEXT: main 1 0 100.00% 11 0 100.00% 0 0 0.00% 0 0 0.00%
|
||||
// REPORT-NEXT: ---
|
||||
// REPORT-NEXT: TOTAL 26 0 100.00% 20 0 100.00% 24 2 91.67% 12 12 0.00%
|
||||
|
||||
// Turn off MC/DC summary.
|
||||
// RUN: llvm-cov report %S/Inputs/mcdc-general.o -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=REPORT_NOMCDC
|
||||
// REPORT_NOMCDC-NOT: TOTAL{{.*}}12 12 0.00%
|
||||
|
||||
|
||||
// Test file-level report.
|
||||
// RUN: llvm-cov report --show-mcdc-summary %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=FILEREPORT
|
||||
// FILEREPORT: TOTAL{{.*}}12 12 0.00%
|
||||
|
||||
|
||||
// Test html output.
|
||||
// RUN: llvm-cov show --show-mcdc-summary --show-mcdc %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp -format html -o %t.html.dir
|
||||
// RUN: FileCheck -check-prefix=HTML -input-file=%t.html.dir/coverage/tmp/mcdc-general.cpp.html %s
|
||||
// HTML-COUNT-4: MC/DC Decision Region (<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: <td class='column-entry-bold'>MC/DC</td>
|
||||
// HTML-INDEX: <a href='coverage{{.*}}mcdc-general.cpp.html'{{.*}}mcdc-general.cpp</a>
|
||||
// HTML-INDEX: <td class='column-entry-green'>
|
||||
// HTML-INDEX: 100.00% (2/2)
|
||||
// HTML-INDEX: 100.00% (20/20)
|
||||
// HTML-INDEX: 100.00% (26/26)
|
||||
// HTML-INDEX: 91.67% (22/24)
|
||||
// HTML-INDEX: 0.00% (0/12)
|
||||
// HTML-INDEX: Totals
|
||||
|
||||
Instructions for regenerating the test:
|
||||
|
||||
# cd %S/Inputs
|
||||
cp mcdc-general.cpp /tmp
|
||||
|
||||
clang -fcoverage-mcdc -fprofile-instr-generate -fcoverage-compilation-dir=. \
|
||||
-fcoverage-mapping /tmp/mcdc-general.cpp -o /tmp/mcdc-general.o
|
||||
|
||||
mv /tmp/mcdc-general.o %S/Inputs
|
||||
148
llvm/test/tools/llvm-cov/mcdc-general.test
Normal file
148
llvm/test/tools/llvm-cov/mcdc-general.test
Normal file
@@ -0,0 +1,148 @@
|
||||
// Test visualization of general MC/DC constructs.
|
||||
|
||||
// RUN: llvm-profdata merge %S/Inputs/mcdc-general.proftext -o %t.profdata
|
||||
// RUN: llvm-cov show --show-mcdc %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s
|
||||
// RUN: llvm-cov report --show-mcdc-summary %S/Inputs/mcdc-general.o -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=REPORT
|
||||
|
||||
// CHECK: test(bool
|
||||
|
||||
// CHECK: ------------------
|
||||
// CHECK-NEXT: |---> MC/DC Decision Region (12:7) to (12:27)
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | Number of Conditions: 4
|
||||
// CHECK-NEXT: | Condition C1 --> (12:8)
|
||||
// CHECK-NEXT: | Condition C2 --> (12:13)
|
||||
// CHECK-NEXT: | Condition C3 --> (12:20)
|
||||
// CHECK-NEXT: | Condition C4 --> (12:25)
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | Executed MC/DC Test Vectors:
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | C1, C2, C3, C4 Result
|
||||
// CHECK-NEXT: | 1 { F, -, F, - = F }
|
||||
// CHECK-NEXT: | 2 { T, F, F, - = F }
|
||||
// CHECK-NEXT: | 3 { F, -, T, F = F }
|
||||
// CHECK-NEXT: | 4 { T, F, T, F = F }
|
||||
// CHECK-NEXT: | 5 { T, T, -, - = T }
|
||||
// CHECK-NEXT: | 6 { T, F, T, T = T }
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | C1-Pair: covered: (1,5)
|
||||
// CHECK-NEXT: | C2-Pair: covered: (2,5)
|
||||
// CHECK-NEXT: | C3-Pair: covered: (2,6)
|
||||
// CHECK-NEXT: | C4-Pair: covered: (4,6)
|
||||
// CHECK-NEXT: | MC/DC Coverage for Decision: 100.00%
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: ------------------
|
||||
|
||||
// CHECK: ------------------
|
||||
// CHECK-NEXT: |---> MC/DC Decision Region (15:7) to (15:13)
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | Number of Conditions: 2
|
||||
// CHECK-NEXT: | Condition C1 --> (15:7)
|
||||
// CHECK-NEXT: | Condition C2 --> (15:12)
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | Executed MC/DC Test Vectors:
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | C1, C2 Result
|
||||
// CHECK-NEXT: | 1 { F, - = F }
|
||||
// CHECK-NEXT: | 2 { T, F = F }
|
||||
// CHECK-NEXT: | 3 { T, T = T }
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | C1-Pair: covered: (1,3)
|
||||
// CHECK-NEXT: | C2-Pair: covered: (2,3)
|
||||
// CHECK-NEXT: | MC/DC Coverage for Decision: 100.00%
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: |---> MC/DC Decision Region (15:19) to (15:25)
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | Number of Conditions: 2
|
||||
// CHECK-NEXT: | Condition C1 --> (15:19)
|
||||
// CHECK-NEXT: | Condition C2 --> (15:24)
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | Executed MC/DC Test Vectors:
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | C1, C2 Result
|
||||
// CHECK-NEXT: | 1 { F, - = F }
|
||||
// CHECK-NEXT: | 2 { T, T = T }
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | C1-Pair: covered: (1,2)
|
||||
// CHECK-NEXT: | C2-Pair: not covered
|
||||
// CHECK-NEXT: | MC/DC Coverage for Decision: 50.00%
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: ------------------
|
||||
|
||||
// CHECK: ------------------
|
||||
// CHECK-NEXT: |---> MC/DC Decision Region (18:7) to (19:15)
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | Number of Conditions: 4
|
||||
// CHECK-NEXT: | Condition C1 --> (18:8)
|
||||
// CHECK-NEXT: | Condition C2 --> (18:13)
|
||||
// CHECK-NEXT: | Condition C3 --> (19:8)
|
||||
// CHECK-NEXT: | Condition C4 --> (19:13)
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | Executed MC/DC Test Vectors:
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | C1, C2, C3, C4 Result
|
||||
// CHECK-NEXT: | 1 { F, -, -, - = F }
|
||||
// CHECK-NEXT: | 2 { T, F, -, - = F }
|
||||
// CHECK-NEXT: | 3 { T, T, T, F = F }
|
||||
// CHECK-NEXT: | 4 { T, T, T, T = T }
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: | C1-Pair: covered: (1,4)
|
||||
// CHECK-NEXT: | C2-Pair: covered: (2,4)
|
||||
// CHECK-NEXT: | C3-Pair: not covered
|
||||
// CHECK-NEXT: | C4-Pair: covered: (3,4)
|
||||
// CHECK-NEXT: | MC/DC Coverage for Decision: 75.00%
|
||||
// CHECK-NEXT: |
|
||||
// CHECK-NEXT: ------------------
|
||||
|
||||
// Turn off MC/DC visualization.
|
||||
// RUN: llvm-cov show %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=NOMCDC
|
||||
// NOMCDC-NOT: MC/DC Decision Region
|
||||
|
||||
// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover MC/DC Conditions Miss Cover
|
||||
// REPORT-NEXT: -------------------------------------------------------------------------------------------------------------------------------------------
|
||||
// REPORT-NEXT: _Z4testbbbb 25 0 100.00% 9 0 100.00% 24 2 91.67% 12 2 83.33%
|
||||
// REPORT-NEXT: main 1 0 100.00% 11 0 100.00% 0 0 0.00% 0 0 0.00%
|
||||
// REPORT-NEXT: ---
|
||||
// REPORT-NEXT: TOTAL 26 0 100.00% 20 0 100.00% 24 2 91.67% 12 2 83.33%
|
||||
|
||||
// Turn off MC/DC summary.
|
||||
// RUN: llvm-cov report %S/Inputs/mcdc-general.o -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=REPORT_NOMCDC
|
||||
// REPORT_NOMCDC-NOT: TOTAL{{.*}}12 2 83.33%
|
||||
|
||||
|
||||
// Test file-level report.
|
||||
// RUN: llvm-cov report --show-mcdc-summary %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=FILEREPORT
|
||||
// FILEREPORT: TOTAL{{.*}}12 2 83.33%
|
||||
|
||||
|
||||
// Test html output.
|
||||
// RUN: llvm-cov show --show-mcdc-summary --show-mcdc %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp -format html -o %t.html.dir
|
||||
// RUN: FileCheck -check-prefix=HTML -input-file=%t.html.dir/coverage/tmp/mcdc-general.cpp.html %s
|
||||
// HTML-COUNT-4: MC/DC Decision Region (<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: <td class='column-entry-bold'>MC/DC</td>
|
||||
// HTML-INDEX: <a href='coverage{{.*}}mcdc-general.cpp.html'{{.*}}mcdc-general.cpp</a>
|
||||
// HTML-INDEX: <td class='column-entry-green'>
|
||||
// HTML-INDEX: 100.00% (2/2)
|
||||
// HTML-INDEX: 100.00% (20/20)
|
||||
// HTML-INDEX: 100.00% (26/26)
|
||||
// HTML-INDEX: 91.67% (22/24)
|
||||
// HTML-INDEX: 83.33% (10/12)
|
||||
// HTML-INDEX: Totals
|
||||
|
||||
Instructions for regenerating the test:
|
||||
|
||||
# cd %S/Inputs
|
||||
cp mcdc-general.cpp /tmp
|
||||
|
||||
clang -fcoverage-mcdc -fprofile-instr-generate -fcoverage-compilation-dir=. \
|
||||
-fcoverage-mapping /tmp/mcdc-general.cpp -o /tmp/mcdc-general.o
|
||||
|
||||
mv /tmp/mcdc-general.o %S/Inputs
|
||||
@@ -105,6 +105,11 @@ private:
|
||||
const MemoryBuffer &File,
|
||||
CoverageData &CoverageInfo);
|
||||
|
||||
/// Create source views for the MCDC records.
|
||||
void attachMCDCSubViews(SourceCoverageView &View, StringRef SourceName,
|
||||
ArrayRef<MCDCRecord> MCDCRecords,
|
||||
const MemoryBuffer &File, CoverageData &CoverageInfo);
|
||||
|
||||
/// Create the source view of a particular function.
|
||||
std::unique_ptr<SourceCoverageView>
|
||||
createFunctionView(const FunctionRecord &Function,
|
||||
@@ -352,6 +357,37 @@ void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View,
|
||||
}
|
||||
}
|
||||
|
||||
void CodeCoverageTool::attachMCDCSubViews(SourceCoverageView &View,
|
||||
StringRef SourceName,
|
||||
ArrayRef<MCDCRecord> MCDCRecords,
|
||||
const MemoryBuffer &File,
|
||||
CoverageData &CoverageInfo) {
|
||||
if (!ViewOpts.ShowMCDC)
|
||||
return;
|
||||
|
||||
const auto *NextRecord = MCDCRecords.begin();
|
||||
const auto *EndRecord = MCDCRecords.end();
|
||||
|
||||
// Group and process MCDC records that have the same line number into the
|
||||
// same subview.
|
||||
while (NextRecord != EndRecord) {
|
||||
std::vector<MCDCRecord> ViewMCDCRecords;
|
||||
unsigned CurrentLine = NextRecord->getDecisionRegion().LineEnd;
|
||||
|
||||
while (NextRecord != EndRecord &&
|
||||
CurrentLine == NextRecord->getDecisionRegion().LineEnd) {
|
||||
ViewMCDCRecords.push_back(*NextRecord++);
|
||||
}
|
||||
|
||||
if (ViewMCDCRecords.empty())
|
||||
continue;
|
||||
|
||||
auto SubView = SourceCoverageView::create(SourceName, File, ViewOpts,
|
||||
std::move(CoverageInfo));
|
||||
View.addMCDCRecord(CurrentLine, ViewMCDCRecords, std::move(SubView));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SourceCoverageView>
|
||||
CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
|
||||
const CoverageMapping &Coverage) {
|
||||
@@ -364,12 +400,15 @@ CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
|
||||
|
||||
auto Branches = FunctionCoverage.getBranches();
|
||||
auto Expansions = FunctionCoverage.getExpansions();
|
||||
auto MCDCRecords = FunctionCoverage.getMCDCRecords();
|
||||
auto View = SourceCoverageView::create(DC.demangle(Function.Name),
|
||||
SourceBuffer.get(), ViewOpts,
|
||||
std::move(FunctionCoverage));
|
||||
attachExpansionSubViews(*View, Expansions, Coverage);
|
||||
attachBranchSubViews(*View, DC.demangle(Function.Name), Branches,
|
||||
SourceBuffer.get(), FunctionCoverage);
|
||||
attachMCDCSubViews(*View, DC.demangle(Function.Name), MCDCRecords,
|
||||
SourceBuffer.get(), FunctionCoverage);
|
||||
|
||||
return View;
|
||||
}
|
||||
@@ -386,11 +425,14 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile,
|
||||
|
||||
auto Branches = FileCoverage.getBranches();
|
||||
auto Expansions = FileCoverage.getExpansions();
|
||||
auto MCDCRecords = FileCoverage.getMCDCRecords();
|
||||
auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
|
||||
ViewOpts, std::move(FileCoverage));
|
||||
attachExpansionSubViews(*View, Expansions, Coverage);
|
||||
attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(),
|
||||
FileCoverage);
|
||||
attachMCDCSubViews(*View, SourceFile, MCDCRecords, SourceBuffer.get(),
|
||||
FileCoverage);
|
||||
if (!ViewOpts.ShowFunctionInstantiations)
|
||||
return View;
|
||||
|
||||
@@ -408,11 +450,14 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile,
|
||||
auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
|
||||
auto SubViewExpansions = SubViewCoverage.getExpansions();
|
||||
auto SubViewBranches = SubViewCoverage.getBranches();
|
||||
auto SubViewMCDCRecords = SubViewCoverage.getMCDCRecords();
|
||||
SubView = SourceCoverageView::create(
|
||||
Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
|
||||
attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
|
||||
attachBranchSubViews(*SubView, SourceFile, SubViewBranches,
|
||||
SourceBuffer.get(), SubViewCoverage);
|
||||
attachMCDCSubViews(*SubView, SourceFile, SubViewMCDCRecords,
|
||||
SourceBuffer.get(), SubViewCoverage);
|
||||
}
|
||||
|
||||
unsigned FileID = Function->CountedRegions.front().FileID;
|
||||
@@ -752,6 +797,10 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
|
||||
cl::desc("Show branch condition statistics in summary table"),
|
||||
cl::init(true));
|
||||
|
||||
cl::opt<bool> MCDCSummary("show-mcdc-summary", cl::Optional,
|
||||
cl::desc("Show MCDC statistics in summary table"),
|
||||
cl::init(false));
|
||||
|
||||
cl::opt<bool> InstantiationSummary(
|
||||
"show-instantiation-summary", cl::Optional,
|
||||
cl::desc("Show instantiation statistics in summary table"));
|
||||
@@ -923,6 +972,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
|
||||
::exit(0);
|
||||
}
|
||||
|
||||
ViewOpts.ShowMCDCSummary = MCDCSummary;
|
||||
ViewOpts.ShowBranchSummary = BranchSummary;
|
||||
ViewOpts.ShowRegionSummary = RegionSummary;
|
||||
ViewOpts.ShowInstantiationSummary = InstantiationSummary;
|
||||
@@ -968,6 +1018,11 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
|
||||
"percent", "Show True/False percent")),
|
||||
cl::init(CoverageViewOptions::BranchOutputType::Off));
|
||||
|
||||
cl::opt<bool> ShowMCDC(
|
||||
"show-mcdc", cl::Optional,
|
||||
cl::desc("Show the MCDC Coverage for each applicable boolean expression"),
|
||||
cl::cat(ViewCategory));
|
||||
|
||||
cl::opt<bool> ShowBestLineRegionsCounts(
|
||||
"show-line-counts-or-regions", cl::Optional,
|
||||
cl::desc("Show the execution counts for each line, or the execution "
|
||||
@@ -1063,6 +1118,7 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
|
||||
ViewOpts.ShowExpandedRegions = ShowExpansions;
|
||||
ViewOpts.ShowBranchCounts =
|
||||
ShowBranches == CoverageViewOptions::BranchOutputType::Count;
|
||||
ViewOpts.ShowMCDC = ShowMCDC;
|
||||
ViewOpts.ShowBranchPercents =
|
||||
ShowBranches == CoverageViewOptions::BranchOutputType::Percent;
|
||||
ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
// -- 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
|
||||
// -- MCDC Records: array => List of MCDC records in the file
|
||||
// -- MCDC Values: array => List of T/F covered condition values
|
||||
// -- Segments: array => List of Segments contained in the file
|
||||
// -- Segment: dict => Describes a segment of the file with a counter
|
||||
// -- Expansions: array => List of expansion records
|
||||
@@ -34,6 +36,7 @@
|
||||
// -- FunctionCoverage: dict => Object summarizing function coverage
|
||||
// -- RegionCoverage: dict => Object summarizing region coverage
|
||||
// -- BranchCoverage: dict => Object summarizing branch coverage
|
||||
// -- MCDCCoverage: dict => Object summarizing MC/DC coverage
|
||||
// -- Functions: array => List of objects describing coverage for functions
|
||||
// -- Function: dict => Coverage info for a single function
|
||||
// -- Filenames: array => List of filenames that the function relates to
|
||||
@@ -43,6 +46,7 @@
|
||||
// -- InstantiationCoverage: dict => Object summarizing inst. coverage
|
||||
// -- RegionCoverage: dict => Object summarizing region coverage
|
||||
// -- BranchCoverage: dict => Object summarizing branch coverage
|
||||
// -- MCDCCoverage: dict => Object summarizing MC/DC coverage
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -97,6 +101,20 @@ json::Array renderBranch(const coverage::CountedRegion &Region) {
|
||||
Region.ExpandedFileID, int64_t(Region.Kind)});
|
||||
}
|
||||
|
||||
json::Array gatherConditions(const coverage::MCDCRecord &Record) {
|
||||
json::Array Conditions;
|
||||
for (unsigned c = 0; c < Record.getNumConditions(); c++)
|
||||
Conditions.push_back(Record.isConditionIndependencePairCovered(c));
|
||||
return Conditions;
|
||||
}
|
||||
|
||||
json::Array renderMCDCRecord(const coverage::MCDCRecord &Record) {
|
||||
const llvm::coverage::CounterMappingRegion &CMR = Record.getDecisionRegion();
|
||||
return json::Array({CMR.LineStart, CMR.ColumnStart, CMR.LineEnd,
|
||||
CMR.ColumnEnd, CMR.ExpandedFileID, int64_t(CMR.Kind),
|
||||
gatherConditions(Record)});
|
||||
}
|
||||
|
||||
json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) {
|
||||
json::Array RegionArray;
|
||||
for (const auto &Region : Regions)
|
||||
@@ -112,6 +130,13 @@ json::Array renderBranchRegions(ArrayRef<coverage::CountedRegion> Regions) {
|
||||
return RegionArray;
|
||||
}
|
||||
|
||||
json::Array renderMCDCRecords(ArrayRef<coverage::MCDCRecord> Records) {
|
||||
json::Array RecordArray;
|
||||
for (auto &Record : Records)
|
||||
RecordArray.push_back(renderMCDCRecord(Record));
|
||||
return RecordArray;
|
||||
}
|
||||
|
||||
std::vector<llvm::coverage::CountedRegion>
|
||||
collectNestedBranches(const coverage::CoverageMapping &Coverage,
|
||||
ArrayRef<llvm::coverage::ExpansionRecord> Expansions) {
|
||||
@@ -178,7 +203,14 @@ json::Object renderSummary(const FileCoverageSummary &Summary) {
|
||||
{"covered", int64_t(Summary.BranchCoverage.getCovered())},
|
||||
{"notcovered", int64_t(Summary.BranchCoverage.getNumBranches() -
|
||||
Summary.BranchCoverage.getCovered())},
|
||||
{"percent", Summary.BranchCoverage.getPercentCovered()}})}});
|
||||
{"percent", Summary.BranchCoverage.getPercentCovered()}})},
|
||||
{"mcdc",
|
||||
json::Object(
|
||||
{{"count", int64_t(Summary.MCDCCoverage.getNumPairs())},
|
||||
{"covered", int64_t(Summary.MCDCCoverage.getCoveredPairs())},
|
||||
{"notcovered", int64_t(Summary.MCDCCoverage.getNumPairs() -
|
||||
Summary.MCDCCoverage.getCoveredPairs())},
|
||||
{"percent", Summary.MCDCCoverage.getPercentCovered()}})}});
|
||||
}
|
||||
|
||||
json::Array renderFileExpansions(const coverage::CoverageMapping &Coverage,
|
||||
@@ -206,6 +238,14 @@ json::Array renderFileBranches(const coverage::CoverageData &FileCoverage,
|
||||
return BranchArray;
|
||||
}
|
||||
|
||||
json::Array renderFileMCDC(const coverage::CoverageData &FileCoverage,
|
||||
const FileCoverageSummary &FileReport) {
|
||||
json::Array MCDCRecordArray;
|
||||
for (const auto &Record : FileCoverage.getMCDCRecords())
|
||||
MCDCRecordArray.push_back(renderMCDCRecord(Record));
|
||||
return MCDCRecordArray;
|
||||
}
|
||||
|
||||
json::Object renderFile(const coverage::CoverageMapping &Coverage,
|
||||
const std::string &Filename,
|
||||
const FileCoverageSummary &FileReport,
|
||||
@@ -216,6 +256,7 @@ json::Object renderFile(const coverage::CoverageMapping &Coverage,
|
||||
auto FileCoverage = Coverage.getCoverageForFile(Filename);
|
||||
File["segments"] = renderFileSegments(FileCoverage, FileReport);
|
||||
File["branches"] = renderFileBranches(FileCoverage, FileReport);
|
||||
File["mcdc_records"] = renderFileMCDC(FileCoverage, FileReport);
|
||||
if (!Options.SkipExpansions) {
|
||||
File["expansions"] =
|
||||
renderFileExpansions(Coverage, FileCoverage, FileReport);
|
||||
@@ -264,6 +305,7 @@ json::Array renderFunctions(
|
||||
{"count", clamp_uint64_to_int64(F.ExecutionCount)},
|
||||
{"regions", renderRegions(F.CountedRegions)},
|
||||
{"branches", renderBranchRegions(F.CountedBranchRegions)},
|
||||
{"mcdc_records", renderMCDCRecords(F.MCDCRecords)},
|
||||
{"filenames", json::Array(F.Filenames)}}));
|
||||
return FunctionArray;
|
||||
}
|
||||
|
||||
@@ -86,9 +86,9 @@ Column column(StringRef Str, unsigned Width, const T &Value) {
|
||||
}
|
||||
|
||||
// Specify the default column widths.
|
||||
size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10, 16,
|
||||
16, 10, 12, 18, 10, 12, 18, 10};
|
||||
size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8, 10, 8, 8};
|
||||
size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10, 16, 16, 10,
|
||||
12, 18, 10, 12, 18, 10, 20, 21, 10};
|
||||
size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8, 10, 8, 8, 20, 8, 8};
|
||||
|
||||
/// Adjust column widths to fit long file paths and function names.
|
||||
void adjustColumnWidths(ArrayRef<StringRef> Files,
|
||||
@@ -291,6 +291,22 @@ void CoverageReport::render(const FileCoverageSummary &File,
|
||||
OS << column("-", FileReportColumns[15], Column::RightAlignment);
|
||||
}
|
||||
|
||||
if (Options.ShowMCDCSummary) {
|
||||
OS << format("%*u", FileReportColumns[16],
|
||||
(unsigned)File.MCDCCoverage.getNumPairs());
|
||||
Options.colored_ostream(OS, LineCoverageColor)
|
||||
<< format("%*u", FileReportColumns[17],
|
||||
(unsigned)(File.MCDCCoverage.getNumPairs() -
|
||||
File.MCDCCoverage.getCoveredPairs()));
|
||||
if (File.MCDCCoverage.getNumPairs())
|
||||
Options.colored_ostream(OS, LineCoverageColor)
|
||||
<< format("%*.2f", FileReportColumns[18] - 1,
|
||||
File.MCDCCoverage.getPercentCovered())
|
||||
<< '%';
|
||||
else
|
||||
OS << column("-", FileReportColumns[18], Column::RightAlignment);
|
||||
}
|
||||
|
||||
OS << "\n";
|
||||
}
|
||||
|
||||
@@ -338,6 +354,19 @@ void CoverageReport::render(const FunctionCoverageSummary &Function,
|
||||
Function.BranchCoverage.getPercentCovered())
|
||||
<< '%';
|
||||
}
|
||||
if (Options.ShowMCDCSummary) {
|
||||
OS << format("%*u", FunctionReportColumns[10],
|
||||
(unsigned)Function.MCDCCoverage.getNumPairs());
|
||||
Options.colored_ostream(OS, LineCoverageColor)
|
||||
<< format("%*u", FunctionReportColumns[11],
|
||||
(unsigned)(Function.MCDCCoverage.getNumPairs() -
|
||||
Function.MCDCCoverage.getCoveredPairs()));
|
||||
Options.colored_ostream(
|
||||
OS, determineCoveragePercentageColor(Function.MCDCCoverage))
|
||||
<< format("%*.2f", FunctionReportColumns[12] - 1,
|
||||
Function.MCDCCoverage.getPercentCovered())
|
||||
<< '%';
|
||||
}
|
||||
OS << "\n";
|
||||
}
|
||||
|
||||
@@ -370,6 +399,11 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
|
||||
OS << column("Branches", FunctionReportColumns[7], Column::RightAlignment)
|
||||
<< column("Miss", FunctionReportColumns[8], Column::RightAlignment)
|
||||
<< column("Cover", FunctionReportColumns[9], Column::RightAlignment);
|
||||
if (Options.ShowMCDCSummary)
|
||||
OS << column("MC/DC Conditions", FunctionReportColumns[10],
|
||||
Column::RightAlignment)
|
||||
<< column("Miss", FunctionReportColumns[11], Column::RightAlignment)
|
||||
<< column("Cover", FunctionReportColumns[12], Column::RightAlignment);
|
||||
OS << "\n";
|
||||
renderDivider(FunctionReportColumns, OS);
|
||||
OS << "\n";
|
||||
@@ -380,6 +414,7 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
|
||||
Totals.RegionCoverage += Function.RegionCoverage;
|
||||
Totals.LineCoverage += Function.LineCoverage;
|
||||
Totals.BranchCoverage += Function.BranchCoverage;
|
||||
Totals.MCDCCoverage += Function.MCDCCoverage;
|
||||
render(Function, DC, OS);
|
||||
}
|
||||
if (Totals.ExecutionCount) {
|
||||
@@ -502,6 +537,12 @@ void CoverageReport::renderFileReports(
|
||||
<< column("Missed Branches", FileReportColumns[14],
|
||||
Column::RightAlignment)
|
||||
<< column("Cover", FileReportColumns[15], Column::RightAlignment);
|
||||
if (Options.ShowMCDCSummary)
|
||||
OS << column("MC/DC Conditions", FileReportColumns[16],
|
||||
Column::RightAlignment)
|
||||
<< column("Missed Conditions", FileReportColumns[17],
|
||||
Column::RightAlignment)
|
||||
<< column("Cover", FileReportColumns[18], Column::RightAlignment);
|
||||
OS << "\n";
|
||||
renderDivider(FileReportColumns, OS);
|
||||
OS << "\n";
|
||||
|
||||
@@ -44,6 +44,21 @@ static void sumBranchExpansions(size_t &NumBranches, size_t &CoveredBranches,
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<size_t, size_t>
|
||||
sumMCDCPairs(const ArrayRef<MCDCRecord> &Records) {
|
||||
size_t NumPairs = 0, CoveredPairs = 0;
|
||||
for (const auto &Record : Records) {
|
||||
const auto NumConditions = Record.getNumConditions();
|
||||
for (unsigned C = 0; C < NumConditions; C++) {
|
||||
if (!Record.isCondFolded(C))
|
||||
++NumPairs;
|
||||
if (Record.isConditionIndependencePairCovered(C))
|
||||
++CoveredPairs;
|
||||
}
|
||||
}
|
||||
return {NumPairs, CoveredPairs};
|
||||
}
|
||||
|
||||
FunctionCoverageSummary
|
||||
FunctionCoverageSummary::get(const CoverageMapping &CM,
|
||||
const coverage::FunctionRecord &Function) {
|
||||
@@ -73,11 +88,15 @@ FunctionCoverageSummary::get(const CoverageMapping &CM,
|
||||
sumBranches(NumBranches, CoveredBranches, CD.getBranches());
|
||||
sumBranchExpansions(NumBranches, CoveredBranches, CM, CD.getExpansions());
|
||||
|
||||
size_t NumPairs = 0, CoveredPairs = 0;
|
||||
std::tie(NumPairs, CoveredPairs) = sumMCDCPairs(CD.getMCDCRecords());
|
||||
|
||||
return FunctionCoverageSummary(
|
||||
Function.Name, Function.ExecutionCount,
|
||||
RegionCoverageInfo(CoveredRegions, NumCodeRegions),
|
||||
LineCoverageInfo(CoveredLines, NumLines),
|
||||
BranchCoverageInfo(CoveredBranches, NumBranches));
|
||||
BranchCoverageInfo(CoveredBranches, NumBranches),
|
||||
MCDCCoverageInfo(CoveredPairs, NumPairs));
|
||||
}
|
||||
|
||||
FunctionCoverageSummary
|
||||
@@ -97,10 +116,12 @@ FunctionCoverageSummary::get(const InstantiationGroup &Group,
|
||||
Summary.RegionCoverage = Summaries[0].RegionCoverage;
|
||||
Summary.LineCoverage = Summaries[0].LineCoverage;
|
||||
Summary.BranchCoverage = Summaries[0].BranchCoverage;
|
||||
Summary.MCDCCoverage = Summaries[0].MCDCCoverage;
|
||||
for (const auto &FCS : Summaries.drop_front()) {
|
||||
Summary.RegionCoverage.merge(FCS.RegionCoverage);
|
||||
Summary.LineCoverage.merge(FCS.LineCoverage);
|
||||
Summary.BranchCoverage.merge(FCS.BranchCoverage);
|
||||
Summary.MCDCCoverage.merge(FCS.MCDCCoverage);
|
||||
}
|
||||
return Summary;
|
||||
}
|
||||
|
||||
@@ -142,6 +142,47 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// Provides information about MC/DC coverage for a function/file.
|
||||
class MCDCCoverageInfo {
|
||||
/// The number of Independence Pairs that were covered.
|
||||
size_t CoveredPairs;
|
||||
|
||||
/// The total number of Independence Pairs in a function/file.
|
||||
size_t NumPairs;
|
||||
|
||||
public:
|
||||
MCDCCoverageInfo() : CoveredPairs(0), NumPairs(0) {}
|
||||
|
||||
MCDCCoverageInfo(size_t CoveredPairs, size_t NumPairs)
|
||||
: CoveredPairs(CoveredPairs), NumPairs(NumPairs) {
|
||||
assert(CoveredPairs <= NumPairs && "Covered pairs over-counted");
|
||||
}
|
||||
|
||||
MCDCCoverageInfo &operator+=(const MCDCCoverageInfo &RHS) {
|
||||
CoveredPairs += RHS.CoveredPairs;
|
||||
NumPairs += RHS.NumPairs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void merge(const MCDCCoverageInfo &RHS) {
|
||||
CoveredPairs = std::max(CoveredPairs, RHS.CoveredPairs);
|
||||
NumPairs = std::max(NumPairs, RHS.NumPairs);
|
||||
}
|
||||
|
||||
size_t getCoveredPairs() const { return CoveredPairs; }
|
||||
|
||||
size_t getNumPairs() const { return NumPairs; }
|
||||
|
||||
bool isFullyCovered() const { return CoveredPairs == NumPairs; }
|
||||
|
||||
double getPercentCovered() const {
|
||||
assert(CoveredPairs <= NumPairs && "Covered pairs over-counted");
|
||||
if (NumPairs == 0)
|
||||
return 0.0;
|
||||
return double(CoveredPairs) / double(NumPairs) * 100.0;
|
||||
}
|
||||
};
|
||||
|
||||
/// Provides information about function coverage for a file.
|
||||
class FunctionCoverageInfo {
|
||||
/// The number of functions that were executed.
|
||||
@@ -189,6 +230,7 @@ struct FunctionCoverageSummary {
|
||||
RegionCoverageInfo RegionCoverage;
|
||||
LineCoverageInfo LineCoverage;
|
||||
BranchCoverageInfo BranchCoverage;
|
||||
MCDCCoverageInfo MCDCCoverage;
|
||||
|
||||
FunctionCoverageSummary(const std::string &Name)
|
||||
: Name(Name), ExecutionCount(0) {}
|
||||
@@ -196,10 +238,11 @@ struct FunctionCoverageSummary {
|
||||
FunctionCoverageSummary(const std::string &Name, uint64_t ExecutionCount,
|
||||
const RegionCoverageInfo &RegionCoverage,
|
||||
const LineCoverageInfo &LineCoverage,
|
||||
const BranchCoverageInfo &BranchCoverage)
|
||||
const BranchCoverageInfo &BranchCoverage,
|
||||
const MCDCCoverageInfo &MCDCCoverage)
|
||||
: Name(Name), ExecutionCount(ExecutionCount),
|
||||
RegionCoverage(RegionCoverage), LineCoverage(LineCoverage),
|
||||
BranchCoverage(BranchCoverage) {}
|
||||
BranchCoverage(BranchCoverage), MCDCCoverage(MCDCCoverage) {}
|
||||
|
||||
/// Compute the code coverage summary for the given function coverage
|
||||
/// mapping record.
|
||||
@@ -219,6 +262,7 @@ struct FileCoverageSummary {
|
||||
RegionCoverageInfo RegionCoverage;
|
||||
LineCoverageInfo LineCoverage;
|
||||
BranchCoverageInfo BranchCoverage;
|
||||
MCDCCoverageInfo MCDCCoverage;
|
||||
FunctionCoverageInfo FunctionCoverage;
|
||||
FunctionCoverageInfo InstantiationCoverage;
|
||||
|
||||
@@ -230,6 +274,7 @@ struct FileCoverageSummary {
|
||||
LineCoverage += RHS.LineCoverage;
|
||||
FunctionCoverage += RHS.FunctionCoverage;
|
||||
BranchCoverage += RHS.BranchCoverage;
|
||||
MCDCCoverage += RHS.MCDCCoverage;
|
||||
InstantiationCoverage += RHS.InstantiationCoverage;
|
||||
return *this;
|
||||
}
|
||||
@@ -238,6 +283,7 @@ struct FileCoverageSummary {
|
||||
RegionCoverage += Function.RegionCoverage;
|
||||
LineCoverage += Function.LineCoverage;
|
||||
BranchCoverage += Function.BranchCoverage;
|
||||
MCDCCoverage += Function.MCDCCoverage;
|
||||
FunctionCoverage.addFunction(/*Covered=*/Function.ExecutionCount > 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,12 +30,14 @@ struct CoverageViewOptions {
|
||||
bool ShowLineNumbers;
|
||||
bool ShowLineStats;
|
||||
bool ShowRegionMarkers;
|
||||
bool ShowMCDC;
|
||||
bool ShowBranchCounts;
|
||||
bool ShowBranchPercents;
|
||||
bool ShowExpandedRegions;
|
||||
bool ShowFunctionInstantiations;
|
||||
bool ShowFullFilenames;
|
||||
bool ShowBranchSummary;
|
||||
bool ShowMCDCSummary;
|
||||
bool ShowRegionSummary;
|
||||
bool ShowInstantiationSummary;
|
||||
bool ShowDirectoryCoverage;
|
||||
|
||||
@@ -178,6 +178,12 @@ void SourceCoverageView::addBranch(unsigned Line,
|
||||
BranchSubViews.emplace_back(Line, Regions, std::move(View));
|
||||
}
|
||||
|
||||
void SourceCoverageView::addMCDCRecord(
|
||||
unsigned Line, ArrayRef<MCDCRecord> Records,
|
||||
std::unique_ptr<SourceCoverageView> View) {
|
||||
MCDCSubViews.emplace_back(Line, Records, std::move(View));
|
||||
}
|
||||
|
||||
void SourceCoverageView::addInstantiation(
|
||||
StringRef FunctionName, unsigned Line,
|
||||
std::unique_ptr<SourceCoverageView> View) {
|
||||
@@ -203,12 +209,15 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
|
||||
llvm::stable_sort(ExpansionSubViews);
|
||||
llvm::stable_sort(InstantiationSubViews);
|
||||
llvm::stable_sort(BranchSubViews);
|
||||
llvm::stable_sort(MCDCSubViews);
|
||||
auto NextESV = ExpansionSubViews.begin();
|
||||
auto EndESV = ExpansionSubViews.end();
|
||||
auto NextISV = InstantiationSubViews.begin();
|
||||
auto EndISV = InstantiationSubViews.end();
|
||||
auto NextBRV = BranchSubViews.begin();
|
||||
auto EndBRV = BranchSubViews.end();
|
||||
auto NextMSV = MCDCSubViews.begin();
|
||||
auto EndMSV = MCDCSubViews.end();
|
||||
|
||||
// Get the coverage information for the file.
|
||||
auto StartSegment = CoverageInfo.begin();
|
||||
@@ -276,6 +285,11 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
|
||||
renderBranchView(OS, *NextBRV, ViewDepth + 1);
|
||||
RenderedSubView = true;
|
||||
}
|
||||
for (; NextMSV != EndMSV && NextMSV->Line == LI.line_number(); ++NextMSV) {
|
||||
renderViewDivider(OS, ViewDepth + 1);
|
||||
renderMCDCView(OS, *NextMSV, ViewDepth + 1);
|
||||
RenderedSubView = true;
|
||||
}
|
||||
if (RenderedSubView)
|
||||
renderViewDivider(OS, ViewDepth + 1);
|
||||
renderLineSuffix(OS, ViewDepth);
|
||||
|
||||
@@ -84,6 +84,23 @@ struct BranchView {
|
||||
}
|
||||
};
|
||||
|
||||
/// A view that represents one or more MCDC regions on a given source line.
|
||||
struct MCDCView {
|
||||
std::vector<MCDCRecord> Records;
|
||||
std::unique_ptr<SourceCoverageView> View;
|
||||
unsigned Line;
|
||||
|
||||
MCDCView(unsigned Line, ArrayRef<MCDCRecord> Records,
|
||||
std::unique_ptr<SourceCoverageView> View)
|
||||
: Records(Records), View(std::move(View)), Line(Line) {}
|
||||
|
||||
unsigned getLine() const { return Line; }
|
||||
|
||||
friend bool operator<(const MCDCView &LHS, const MCDCView &RHS) {
|
||||
return LHS.Line < RHS.Line;
|
||||
}
|
||||
};
|
||||
|
||||
/// A file manager that handles format-aware file creation.
|
||||
class CoveragePrinter {
|
||||
public:
|
||||
@@ -160,6 +177,9 @@ class SourceCoverageView {
|
||||
/// A container for all branches in the source on display.
|
||||
std::vector<BranchView> BranchSubViews;
|
||||
|
||||
/// A container for all MCDC records in the source on display.
|
||||
std::vector<MCDCView> MCDCSubViews;
|
||||
|
||||
/// A container for all instantiations (e.g template functions) in the source
|
||||
/// on display.
|
||||
std::vector<InstantiationView> InstantiationSubViews;
|
||||
@@ -233,6 +253,10 @@ protected:
|
||||
virtual void renderBranchView(raw_ostream &OS, BranchView &BRV,
|
||||
unsigned ViewDepth) = 0;
|
||||
|
||||
/// Render an MCDC view.
|
||||
virtual void renderMCDCView(raw_ostream &OS, MCDCView &BRV,
|
||||
unsigned ViewDepth) = 0;
|
||||
|
||||
/// Render \p Title, a project title if one is available, and the
|
||||
/// created time.
|
||||
virtual void renderTitle(raw_ostream &OS, StringRef CellText) = 0;
|
||||
@@ -283,6 +307,10 @@ public:
|
||||
void addBranch(unsigned Line, ArrayRef<CountedRegion> Regions,
|
||||
std::unique_ptr<SourceCoverageView> View);
|
||||
|
||||
/// Add an MCDC subview to this view.
|
||||
void addMCDCRecord(unsigned Line, ArrayRef<MCDCRecord> Records,
|
||||
std::unique_ptr<SourceCoverageView> View);
|
||||
|
||||
/// Print the code coverage information for a specific portion of a
|
||||
/// source file to the output stream.
|
||||
void print(raw_ostream &OS, bool WholeFile, bool ShowSourceName,
|
||||
|
||||
@@ -335,6 +335,10 @@ void emitTableRow(raw_ostream &OS, const CoverageViewOptions &Opts,
|
||||
AddCoverageTripleToColumn(FCS.BranchCoverage.getCovered(),
|
||||
FCS.BranchCoverage.getNumBranches(),
|
||||
FCS.BranchCoverage.getPercentCovered());
|
||||
if (Opts.ShowMCDCSummary)
|
||||
AddCoverageTripleToColumn(FCS.MCDCCoverage.getCoveredPairs(),
|
||||
FCS.MCDCCoverage.getNumPairs(),
|
||||
FCS.MCDCCoverage.getPercentCovered());
|
||||
|
||||
if (IsTotals)
|
||||
OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row-bold");
|
||||
@@ -385,6 +389,8 @@ static void emitColumnLabelsForIndex(raw_ostream &OS,
|
||||
Columns.emplace_back(tag("td", "Region Coverage", "column-entry-bold"));
|
||||
if (Opts.ShowBranchSummary)
|
||||
Columns.emplace_back(tag("td", "Branch Coverage", "column-entry-bold"));
|
||||
if (Opts.ShowMCDCSummary)
|
||||
Columns.emplace_back(tag("td", "MC/DC", "column-entry-bold"));
|
||||
OS << tag("tr", join(Columns.begin(), Columns.end(), ""));
|
||||
}
|
||||
|
||||
@@ -955,6 +961,52 @@ void SourceCoverageViewHTML::renderBranchView(raw_ostream &OS, BranchView &BRV,
|
||||
OS << EndExpansionDiv;
|
||||
}
|
||||
|
||||
void SourceCoverageViewHTML::renderMCDCView(raw_ostream &OS, MCDCView &MRV,
|
||||
unsigned ViewDepth) {
|
||||
for (auto &Record : MRV.Records) {
|
||||
OS << BeginExpansionDiv;
|
||||
OS << BeginPre;
|
||||
OS << " MC/DC Decision Region (";
|
||||
|
||||
// Display Line + Column information.
|
||||
const CounterMappingRegion &DecisionRegion = Record.getDecisionRegion();
|
||||
std::string LineNoStr = Twine(DecisionRegion.LineStart).str();
|
||||
std::string ColNoStr = Twine(DecisionRegion.ColumnStart).str();
|
||||
std::string TargetName = "L" + LineNoStr;
|
||||
OS << tag("span",
|
||||
a("#" + TargetName, tag("span", LineNoStr + ":" + ColNoStr),
|
||||
TargetName),
|
||||
"line-number") +
|
||||
") to (";
|
||||
LineNoStr = utostr(uint64_t(DecisionRegion.LineEnd));
|
||||
ColNoStr = utostr(uint64_t(DecisionRegion.ColumnEnd));
|
||||
OS << tag("span",
|
||||
a("#" + TargetName, tag("span", LineNoStr + ":" + ColNoStr),
|
||||
TargetName),
|
||||
"line-number") +
|
||||
")\n\n";
|
||||
|
||||
// Display MC/DC Information.
|
||||
OS << " Number of Conditions: " << Record.getNumConditions() << "\n";
|
||||
for (unsigned i = 0; i < Record.getNumConditions(); i++) {
|
||||
OS << " " << Record.getConditionHeaderString(i);
|
||||
}
|
||||
OS << "\n";
|
||||
OS << " Executed MC/DC Test Vectors:\n\n ";
|
||||
OS << Record.getTestVectorHeaderString();
|
||||
for (unsigned i = 0; i < Record.getNumTestVectors(); i++)
|
||||
OS << Record.getTestVectorString(i);
|
||||
OS << "\n";
|
||||
for (unsigned i = 0; i < Record.getNumConditions(); i++)
|
||||
OS << Record.getConditionCoverageString(i);
|
||||
OS << " MC/DC Coverage for Expression: ";
|
||||
OS << format("%0.2f", Record.getPercentCovered()) << "%\n";
|
||||
OS << EndPre;
|
||||
OS << EndExpansionDiv;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS,
|
||||
InstantiationView &ISV,
|
||||
unsigned ViewDepth) {
|
||||
|
||||
@@ -91,6 +91,9 @@ class SourceCoverageViewHTML : public SourceCoverageView {
|
||||
void renderBranchView(raw_ostream &OS, BranchView &BRV,
|
||||
unsigned ViewDepth) override;
|
||||
|
||||
void renderMCDCView(raw_ostream &OS, MCDCView &BRV,
|
||||
unsigned ViewDepth) override;
|
||||
|
||||
void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
|
||||
unsigned ViewDepth) override;
|
||||
|
||||
|
||||
@@ -337,6 +337,57 @@ void SourceCoverageViewText::renderBranchView(raw_ostream &OS, BranchView &BRV,
|
||||
}
|
||||
}
|
||||
|
||||
void SourceCoverageViewText::renderMCDCView(raw_ostream &OS, MCDCView &MRV,
|
||||
unsigned ViewDepth) {
|
||||
for (auto &Record : MRV.Records) {
|
||||
renderLinePrefix(OS, ViewDepth);
|
||||
OS << "---> MC/DC Decision Region (";
|
||||
// Display Line + Column information.
|
||||
const CounterMappingRegion &DecisionRegion = Record.getDecisionRegion();
|
||||
OS << DecisionRegion.LineStart << ":";
|
||||
OS << DecisionRegion.ColumnStart << ") to (";
|
||||
OS << DecisionRegion.LineEnd << ":";
|
||||
OS << DecisionRegion.ColumnEnd << ")\n";
|
||||
renderLinePrefix(OS, ViewDepth);
|
||||
OS << "\n";
|
||||
|
||||
// Display MC/DC Information.
|
||||
renderLinePrefix(OS, ViewDepth);
|
||||
OS << " Number of Conditions: " << Record.getNumConditions() << "\n";
|
||||
for (unsigned i = 0; i < Record.getNumConditions(); i++) {
|
||||
renderLinePrefix(OS, ViewDepth);
|
||||
OS << " " << Record.getConditionHeaderString(i);
|
||||
}
|
||||
renderLinePrefix(OS, ViewDepth);
|
||||
OS << "\n";
|
||||
renderLinePrefix(OS, ViewDepth);
|
||||
OS << " Executed MC/DC Test Vectors:\n";
|
||||
renderLinePrefix(OS, ViewDepth);
|
||||
OS << "\n";
|
||||
renderLinePrefix(OS, ViewDepth);
|
||||
OS << " ";
|
||||
OS << Record.getTestVectorHeaderString();
|
||||
for (unsigned i = 0; i < Record.getNumTestVectors(); i++) {
|
||||
renderLinePrefix(OS, ViewDepth);
|
||||
OS << Record.getTestVectorString(i);
|
||||
}
|
||||
renderLinePrefix(OS, ViewDepth);
|
||||
OS << "\n";
|
||||
for (unsigned i = 0; i < Record.getNumConditions(); i++) {
|
||||
renderLinePrefix(OS, ViewDepth);
|
||||
OS << Record.getConditionCoverageString(i);
|
||||
}
|
||||
renderLinePrefix(OS, ViewDepth);
|
||||
OS << " MC/DC Coverage for Decision: ";
|
||||
colored_ostream(OS, raw_ostream::RED,
|
||||
getOptions().Colors && Record.getPercentCovered() < 100.0,
|
||||
/*Bold=*/false, /*BG=*/true)
|
||||
<< format("%0.2f", Record.getPercentCovered()) << "%\n";
|
||||
renderLinePrefix(OS, ViewDepth);
|
||||
OS << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,
|
||||
InstantiationView &ISV,
|
||||
unsigned ViewDepth) {
|
||||
|
||||
@@ -77,6 +77,9 @@ class SourceCoverageViewText : public SourceCoverageView {
|
||||
void renderBranchView(raw_ostream &OS, BranchView &BRV,
|
||||
unsigned ViewDepth) override;
|
||||
|
||||
void renderMCDCView(raw_ostream &OS, MCDCView &BRV,
|
||||
unsigned ViewDepth) override;
|
||||
|
||||
void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
|
||||
unsigned ViewDepth) override;
|
||||
|
||||
|
||||
@@ -187,6 +187,25 @@ struct CoverageMappingTest : ::testing::TestWithParam<std::tuple<bool, bool>> {
|
||||
: CounterMappingRegion::makeRegion(C, FileID, LS, CS, LE, CE));
|
||||
}
|
||||
|
||||
void addMCDCDecisionCMR(unsigned Mask, unsigned NC, StringRef File,
|
||||
unsigned LS, unsigned CS, unsigned LE, unsigned CE) {
|
||||
auto &Regions = InputFunctions.back().Regions;
|
||||
unsigned FileID = getFileIndexForFunction(File);
|
||||
Regions.push_back(CounterMappingRegion::makeDecisionRegion(
|
||||
CounterMappingRegion::MCDCParameters{Mask, NC}, FileID, LS, CS, LE,
|
||||
CE));
|
||||
}
|
||||
|
||||
void addMCDCBranchCMR(Counter C1, Counter C2, unsigned ID, unsigned TrueID,
|
||||
unsigned FalseID, StringRef File, unsigned LS,
|
||||
unsigned CS, unsigned LE, unsigned CE) {
|
||||
auto &Regions = InputFunctions.back().Regions;
|
||||
unsigned FileID = getFileIndexForFunction(File);
|
||||
Regions.push_back(CounterMappingRegion::makeBranchRegion(
|
||||
C1, C2, CounterMappingRegion::MCDCParameters{0, 0, ID, TrueID, FalseID},
|
||||
FileID, LS, CS, LE, CE));
|
||||
}
|
||||
|
||||
void addExpansionCMR(StringRef File, StringRef ExpandedFile, unsigned LS,
|
||||
unsigned CS, unsigned LE, unsigned CE) {
|
||||
InputFunctions.back().Regions.push_back(CounterMappingRegion::makeExpansion(
|
||||
@@ -828,6 +847,33 @@ TEST_P(CoverageMappingTest, non_code_region_counters) {
|
||||
ASSERT_EQ(1U, Names.size());
|
||||
}
|
||||
|
||||
// Test that MCDC bitmasks not associated with any code regions are allowed.
|
||||
TEST_P(CoverageMappingTest, non_code_region_bitmask) {
|
||||
// No records in profdata
|
||||
|
||||
startFunction("func", 0x1234);
|
||||
addCMR(Counter::getCounter(0), "file", 1, 1, 5, 5);
|
||||
addCMR(Counter::getCounter(1), "file", 1, 1, 5, 5);
|
||||
addCMR(Counter::getCounter(2), "file", 1, 1, 5, 5);
|
||||
addCMR(Counter::getCounter(3), "file", 1, 1, 5, 5);
|
||||
|
||||
addMCDCDecisionCMR(0, 2, "file", 7, 1, 7, 6);
|
||||
addMCDCBranchCMR(Counter::getCounter(0), Counter::getCounter(1), 1, 2, 0,
|
||||
"file", 7, 2, 7, 3);
|
||||
addMCDCBranchCMR(Counter::getCounter(2), Counter::getCounter(3), 2, 0, 0,
|
||||
"file", 7, 4, 7, 5);
|
||||
|
||||
EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
|
||||
|
||||
std::vector<std::string> Names;
|
||||
for (const auto &Func : LoadedCoverage->getCoveredFunctions()) {
|
||||
Names.push_back(Func.Name);
|
||||
ASSERT_EQ(2U, Func.CountedBranchRegions.size());
|
||||
ASSERT_EQ(1U, Func.MCDCRecords.size());
|
||||
}
|
||||
ASSERT_EQ(1U, Names.size());
|
||||
}
|
||||
|
||||
TEST_P(CoverageMappingTest, strip_filename_prefix) {
|
||||
ProfileWriter.addRecord({"file1:func", 0x1234, {0}}, Err);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user