Skip to content

Commit 618a221

Browse files
committed
[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
1 parent 33dfd90 commit 618a221

33 files changed

+2216
-22
lines changed

llvm/docs/CommandGuide/llvm-cov.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ OPTIONS
222222
Show coverage for branch conditions in terms of either count or percentage.
223223
The supported views are: "count", "percent".
224224

225+
.. option:: -show-mcdc
226+
227+
Show modified condition/decision coverage (MC/DC) for each complex condition.
228+
This should be used in addition to branch coverage.
229+
225230
.. option:: -show-line-counts
226231

227232
Show the execution counts for each line. Defaults to true, unless another
@@ -426,6 +431,11 @@ OPTIONS
426431

427432
Show statistics for all branch conditions. Defaults to true.
428433

434+
.. option:: -show-mcdc-summary
435+
436+
Show modified condition/decision coverage (MC/DC) statistics for complex
437+
conditions. Defaults to false.
438+
429439
.. option:: -show-functions
430440

431441
Show coverage summaries for each function. Defaults to false.

llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h

Lines changed: 243 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPING_H
1616

1717
#include "llvm/ADT/ArrayRef.h"
18+
#include "llvm/ADT/BitVector.h"
1819
#include "llvm/ADT/DenseMap.h"
1920
#include "llvm/ADT/DenseSet.h"
2021
#include "llvm/ADT/Hashing.h"
@@ -33,6 +34,7 @@
3334
#include <cstdint>
3435
#include <iterator>
3536
#include <memory>
37+
#include <sstream>
3638
#include <string>
3739
#include <system_error>
3840
#include <tuple>
@@ -237,7 +239,28 @@ struct CounterMappingRegion {
237239
/// A BranchRegion represents leaf-level boolean expressions and is
238240
/// associated with two counters, each representing the number of times the
239241
/// expression evaluates to true or false.
240-
BranchRegion
242+
BranchRegion,
243+
244+
/// A DecisionRegion represents a top-level boolean expression and is
245+
/// associated with a variable length bitmap index and condition number.
246+
MCDCDecisionRegion,
247+
248+
/// A Branch Region can be extended to include IDs to facilitate MC/DC.
249+
MCDCBranchRegion
250+
};
251+
252+
using MCDCConditionID = unsigned int;
253+
struct MCDCParameters {
254+
/// Byte Index of Bitmap Coverage Object for a Decision Region (MC/DC
255+
/// only).
256+
unsigned BitmapIdx = 0;
257+
258+
/// Number of Conditions used for a Decision Region (MC/DC only).
259+
unsigned NumConditions = 0;
260+
261+
/// IDs used to represent a branch region and other branch regions
262+
/// evaluated based on True and False branches (MC/DC only).
263+
MCDCConditionID ID = 0, TrueID = 0, FalseID = 0;
241264
};
242265

243266
/// Primary Counter that is also used for Branch Regions (TrueCount).
@@ -246,8 +269,13 @@ struct CounterMappingRegion {
246269
/// Secondary Counter used for Branch Regions (FalseCount).
247270
Counter FalseCount;
248271

249-
unsigned FileID, ExpandedFileID;
272+
/// Parameters used for Modified Condition/Decision Coverage
273+
MCDCParameters MCDCParams;
274+
275+
unsigned FileID = 0;
276+
unsigned ExpandedFileID = 0;
250277
unsigned LineStart, ColumnStart, LineEnd, ColumnEnd;
278+
251279
RegionKind Kind;
252280

253281
CounterMappingRegion(Counter Count, unsigned FileID, unsigned ExpandedFileID,
@@ -257,15 +285,24 @@ struct CounterMappingRegion {
257285
LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd),
258286
ColumnEnd(ColumnEnd), Kind(Kind) {}
259287

260-
CounterMappingRegion(Counter Count, Counter FalseCount, unsigned FileID,
288+
CounterMappingRegion(Counter Count, Counter FalseCount,
289+
MCDCParameters MCDCParams, unsigned FileID,
261290
unsigned ExpandedFileID, unsigned LineStart,
262291
unsigned ColumnStart, unsigned LineEnd,
263292
unsigned ColumnEnd, RegionKind Kind)
264-
: Count(Count), FalseCount(FalseCount), FileID(FileID),
265-
ExpandedFileID(ExpandedFileID), LineStart(LineStart),
293+
: Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams),
294+
FileID(FileID), ExpandedFileID(ExpandedFileID), LineStart(LineStart),
266295
ColumnStart(ColumnStart), LineEnd(LineEnd), ColumnEnd(ColumnEnd),
267296
Kind(Kind) {}
268297

298+
CounterMappingRegion(MCDCParameters MCDCParams, unsigned FileID,
299+
unsigned ExpandedFileID, unsigned LineStart,
300+
unsigned ColumnStart, unsigned LineEnd,
301+
unsigned ColumnEnd, RegionKind Kind)
302+
: MCDCParams(MCDCParams), ExpandedFileID(ExpandedFileID),
303+
LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd),
304+
ColumnEnd(ColumnEnd), Kind(Kind) {}
305+
269306
static CounterMappingRegion
270307
makeRegion(Counter Count, unsigned FileID, unsigned LineStart,
271308
unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) {
@@ -299,8 +336,27 @@ struct CounterMappingRegion {
299336
makeBranchRegion(Counter Count, Counter FalseCount, unsigned FileID,
300337
unsigned LineStart, unsigned ColumnStart, unsigned LineEnd,
301338
unsigned ColumnEnd) {
302-
return CounterMappingRegion(Count, FalseCount, FileID, 0, LineStart,
303-
ColumnStart, LineEnd, ColumnEnd, BranchRegion);
339+
return CounterMappingRegion(Count, FalseCount, MCDCParameters(), FileID, 0,
340+
LineStart, ColumnStart, LineEnd, ColumnEnd,
341+
BranchRegion);
342+
}
343+
344+
static CounterMappingRegion
345+
makeBranchRegion(Counter Count, Counter FalseCount, MCDCParameters MCDCParams,
346+
unsigned FileID, unsigned LineStart, unsigned ColumnStart,
347+
unsigned LineEnd, unsigned ColumnEnd) {
348+
return CounterMappingRegion(Count, FalseCount, MCDCParams, FileID, 0,
349+
LineStart, ColumnStart, LineEnd, ColumnEnd,
350+
MCDCParams.ID == 0 ? BranchRegion
351+
: MCDCBranchRegion);
352+
}
353+
354+
static CounterMappingRegion
355+
makeDecisionRegion(MCDCParameters MCDCParams, unsigned FileID,
356+
unsigned LineStart, unsigned ColumnStart, unsigned LineEnd,
357+
unsigned ColumnEnd) {
358+
return CounterMappingRegion(MCDCParams, FileID, 0, LineStart, ColumnStart,
359+
LineEnd, ColumnEnd, MCDCDecisionRegion);
304360
}
305361

306362
inline LineColPair startLoc() const {
@@ -326,18 +382,177 @@ struct CountedRegion : public CounterMappingRegion {
326382
FalseExecutionCount(FalseExecutionCount), Folded(false) {}
327383
};
328384

385+
/// MCDC Record grouping all information together.
386+
struct MCDCRecord {
387+
enum CondState { MCDC_DontCare = -1, MCDC_False = 0, MCDC_True = 1 };
388+
389+
using TestVector = llvm::SmallVector<CondState>;
390+
using TestVectors = llvm::SmallVector<TestVector>;
391+
using BoolVector = llvm::SmallVector<bool>;
392+
using TVRowPair = std::pair<unsigned, unsigned>;
393+
using TVPairMap = llvm::DenseMap<unsigned, TVRowPair>;
394+
using CondIDMap = llvm::DenseMap<unsigned, unsigned>;
395+
using LineColPairMap = llvm::DenseMap<unsigned, LineColPair>;
396+
397+
private:
398+
CounterMappingRegion Region;
399+
TestVectors TV;
400+
TVPairMap IndependencePairs;
401+
BoolVector Folded;
402+
CondIDMap PosToID;
403+
LineColPairMap CondLoc;
404+
405+
public:
406+
MCDCRecord(CounterMappingRegion Region, TestVectors TV,
407+
TVPairMap IndependencePairs, BoolVector Folded, CondIDMap PosToID,
408+
LineColPairMap CondLoc)
409+
: Region(Region), TV(TV), IndependencePairs(IndependencePairs),
410+
Folded(Folded), PosToID(PosToID), CondLoc(CondLoc){};
411+
412+
CounterMappingRegion getDecisionRegion() const { return Region; }
413+
unsigned getNumConditions() const { return Region.MCDCParams.NumConditions; }
414+
unsigned getNumTestVectors() const { return TV.size(); }
415+
bool isCondFolded(unsigned Condition) const { return Folded[Condition]; }
416+
417+
CondState getTVCondition(unsigned TestVectorIndex, unsigned Condition) {
418+
// Accessing conditions in the TestVectors requires a translation from a
419+
// ordinal position to actual condition ID. This is done via PosToID[].
420+
return TV[TestVectorIndex][PosToID[Condition]];
421+
}
422+
423+
CondState getTVResult(unsigned TestVectorIndex) {
424+
// The last value for a Test Vector, after its constituent conditions, is
425+
// always the Result. See MCDCRecordProcessor::RecordTestVector().
426+
return TV[TestVectorIndex][getNumConditions()];
427+
}
428+
429+
bool isConditionIndependencePairCovered(unsigned Condition) const {
430+
// Accessing conditions in the TestVector Row Pairs requires a translation
431+
// from a ordinal position to actual condition ID. This is done via
432+
// PosToID[].
433+
auto It = PosToID.find(Condition);
434+
if (It != PosToID.end())
435+
return (IndependencePairs.find(It->second) != IndependencePairs.end());
436+
llvm_unreachable("Condition ID without an Ordinal mapping");
437+
}
438+
439+
TVRowPair getConditionIndependencePair(unsigned Condition) {
440+
// Accessing conditions in the TestVector Row Pairs requires a translation
441+
// from a ordinal position to actual condition ID. This is done via
442+
// PosToID[].
443+
assert(isConditionIndependencePairCovered(Condition));
444+
return IndependencePairs[PosToID[Condition]];
445+
}
446+
447+
float getPercentCovered() const {
448+
unsigned Folded = 0;
449+
unsigned Covered = 0;
450+
for (unsigned C = 0; C < getNumConditions(); C++) {
451+
if (isCondFolded(C))
452+
Folded++;
453+
else if (isConditionIndependencePairCovered(C))
454+
Covered++;
455+
}
456+
457+
unsigned Total = getNumConditions() - Folded;
458+
if (Total == 0)
459+
return 0.0;
460+
return (static_cast<double>(Covered) / static_cast<double>(Total)) * 100.0;
461+
}
462+
463+
std::string getConditionHeaderString(unsigned Condition) {
464+
std::ostringstream OS;
465+
OS << "Condition C" << Condition + 1 << " --> (";
466+
OS << CondLoc[Condition].first << ":" << CondLoc[Condition].second;
467+
OS << ")\n";
468+
return OS.str();
469+
}
470+
471+
std::string getTestVectorHeaderString() {
472+
std::ostringstream OS;
473+
if (getNumTestVectors() == 0) {
474+
OS << "None.\n";
475+
return OS.str();
476+
}
477+
for (unsigned I = 0; I < getNumConditions(); I++) {
478+
OS << "C" << I + 1;
479+
if (I != getNumConditions() - 1)
480+
OS << ", ";
481+
}
482+
OS << " Result\n";
483+
return OS.str();
484+
}
485+
486+
std::string getTestVectorString(unsigned TestVectorIndex) {
487+
assert(TestVectorIndex < getNumTestVectors() &&
488+
"TestVector index out of bounds!");
489+
std::ostringstream OS;
490+
// Add individual condition values to the string.
491+
OS << " " << TestVectorIndex + 1 << " { ";
492+
for (unsigned Condition = 0; Condition < getNumConditions(); Condition++) {
493+
if (isCondFolded(Condition))
494+
OS << "C";
495+
else {
496+
switch (getTVCondition(TestVectorIndex, Condition)) {
497+
case MCDCRecord::MCDC_DontCare:
498+
OS << "-";
499+
break;
500+
case MCDCRecord::MCDC_True:
501+
OS << "T";
502+
break;
503+
case MCDCRecord::MCDC_False:
504+
OS << "F";
505+
break;
506+
}
507+
}
508+
if (Condition != getNumConditions() - 1)
509+
OS << ", ";
510+
}
511+
512+
// Add result value to the string.
513+
OS << " = ";
514+
if (getTVResult(TestVectorIndex) == MCDC_True)
515+
OS << "T";
516+
else
517+
OS << "F";
518+
OS << " }\n";
519+
520+
return OS.str();
521+
}
522+
523+
std::string getConditionCoverageString(unsigned Condition) {
524+
assert(Condition < getNumConditions() &&
525+
"Condition index is out of bounds!");
526+
std::ostringstream OS;
527+
528+
OS << " C" << Condition + 1 << "-Pair: ";
529+
if (isCondFolded(Condition)) {
530+
OS << "constant folded\n";
531+
} else if (isConditionIndependencePairCovered(Condition)) {
532+
TVRowPair rows = getConditionIndependencePair(Condition);
533+
OS << "covered: (" << rows.first << ",";
534+
OS << rows.second << ")\n";
535+
} else
536+
OS << "not covered\n";
537+
538+
return OS.str();
539+
}
540+
};
541+
329542
/// A Counter mapping context is used to connect the counters, expressions
330543
/// and the obtained counter values.
331544
class CounterMappingContext {
332545
ArrayRef<CounterExpression> Expressions;
333546
ArrayRef<uint64_t> CounterValues;
547+
ArrayRef<uint8_t> BitmapBytes;
334548

335549
public:
336550
CounterMappingContext(ArrayRef<CounterExpression> Expressions,
337551
ArrayRef<uint64_t> CounterValues = std::nullopt)
338552
: Expressions(Expressions), CounterValues(CounterValues) {}
339553

340554
void setCounts(ArrayRef<uint64_t> Counts) { CounterValues = Counts; }
555+
void setBitmapBytes(ArrayRef<uint8_t> Bytes) { BitmapBytes = Bytes; }
341556

342557
void dump(const Counter &C, raw_ostream &OS) const;
343558
void dump(const Counter &C) const { dump(C, dbgs()); }
@@ -346,6 +561,17 @@ class CounterMappingContext {
346561
/// counter was executed.
347562
Expected<int64_t> evaluate(const Counter &C) const;
348563

564+
/// Return the number of times that a region of code associated with this
565+
/// counter was executed.
566+
Expected<BitVector>
567+
evaluateBitmap(const CounterMappingRegion *MCDCDecision) const;
568+
569+
/// Return an MCDC record that indicates executed test vectors and condition
570+
/// pairs.
571+
Expected<MCDCRecord>
572+
evaluateMCDCRegion(CounterMappingRegion Region, BitVector Bitmap,
573+
ArrayRef<CounterMappingRegion> Branches);
574+
349575
unsigned getMaxCounterID(const Counter &C) const;
350576
};
351577

@@ -364,6 +590,8 @@ struct FunctionRecord {
364590
std::vector<CountedRegion> CountedRegions;
365591
/// Branch Regions in the function along with their counts.
366592
std::vector<CountedRegion> CountedBranchRegions;
593+
/// MCDC Records record a DecisionRegion and associated BranchRegions.
594+
std::vector<MCDCRecord> MCDCRecords;
367595
/// The number of times this function was executed.
368596
uint64_t ExecutionCount = 0;
369597

@@ -373,9 +601,12 @@ struct FunctionRecord {
373601
FunctionRecord(FunctionRecord &&FR) = default;
374602
FunctionRecord &operator=(FunctionRecord &&) = default;
375603

604+
void pushMCDCRecord(MCDCRecord Record) { MCDCRecords.push_back(Record); }
605+
376606
void pushRegion(CounterMappingRegion Region, uint64_t Count,
377607
uint64_t FalseCount) {
378-
if (Region.Kind == CounterMappingRegion::BranchRegion) {
608+
if (Region.Kind == CounterMappingRegion::BranchRegion ||
609+
Region.Kind == CounterMappingRegion::MCDCBranchRegion) {
379610
CountedBranchRegions.emplace_back(Region, Count, FalseCount);
380611
// If both counters are hard-coded to zero, then this region represents a
381612
// constant-folded branch.
@@ -546,6 +777,7 @@ class CoverageData {
546777
std::vector<CoverageSegment> Segments;
547778
std::vector<ExpansionRecord> Expansions;
548779
std::vector<CountedRegion> BranchRegions;
780+
std::vector<MCDCRecord> MCDCRecords;
549781

550782
public:
551783
CoverageData() = default;
@@ -572,6 +804,9 @@ class CoverageData {
572804

573805
/// Branches that can be further processed.
574806
ArrayRef<CountedRegion> getBranches() const { return BranchRegions; }
807+
808+
/// MCDC Records that can be further processed.
809+
ArrayRef<MCDCRecord> getMCDCRecords() const { return MCDCRecords; }
575810
};
576811

577812
/// The mapping of profile information to coverage data.

0 commit comments

Comments
 (0)