From ee51de9836894f829a0811307901434ee592c9be Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 6 Apr 2026 22:59:14 +0200 Subject: [PATCH] [llvm-cov] add ability to show non executed test vectors for mc/dc coverage (#187517) - Added `-show-mcdc-non-executed-vectors` option - Non-executed test vectors now are tracked - When the opt is present it's get written to UI --- .../ProfileData/Coverage/CoverageMapping.h | 99 +++++++----- .../ProfileData/Coverage/CoverageMapping.cpp | 60 ++++--- llvm/test/tools/llvm-cov/mcdc-const.test | 147 +++++++++++++++++- .../test/tools/llvm-cov/mcdc-export-json.test | 22 ++- .../tools/llvm-cov/mcdc-general-none.test | 124 ++++++++++++++- llvm/test/tools/llvm-cov/mcdc-general.test | 136 +++++++++++++++- llvm/test/tools/llvm-cov/mcdc-macro.test | 136 +++++++++++++++- llvm/tools/llvm-cov/CodeCoverage.cpp | 13 ++ llvm/tools/llvm-cov/CoverageExporterJson.cpp | 58 ++++--- llvm/tools/llvm-cov/CoverageViewOptions.h | 1 + .../tools/llvm-cov/SourceCoverageViewHTML.cpp | 33 +++- .../tools/llvm-cov/SourceCoverageViewText.cpp | 49 +++++- 12 files changed, 774 insertions(+), 104 deletions(-) diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h index 8ab036195160..96fb5f337b07 100644 --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h @@ -459,15 +459,56 @@ struct MCDCRecord { private: CounterMappingRegion Region; TestVectors TV; + TestVectors NotExecutedTV; std::optional IndependencePairs; BoolVector Folded; CondIDMap PosToID; LineColPairMap CondLoc; + std::string formatTestVectorRow(const TestVector &Vec, CondState Result, + unsigned DisplayRowNumber) const { + std::ostringstream OS; + const auto NumConditions = getNumConditions(); + // Add individual condition values to the string. + OS << " " << DisplayRowNumber << " { "; + for (unsigned Condition = 0; Condition < NumConditions; Condition++) { + if (isCondFolded(Condition)) + OS << "C"; + else { + auto It = PosToID.find(Condition); + assert(It != PosToID.end() && "Ordinal without CondID mapping"); + switch (Vec[It->second]) { + case MCDC_DontCare: + OS << "-"; + break; + case MCDC_True: + OS << "T"; + break; + case MCDC_False: + OS << "F"; + break; + } + } + if (Condition != NumConditions - 1) + OS << ", "; + } + + // Add result value to the string. + OS << " = "; + if (Result == MCDC_True) + OS << "T"; + else + OS << "F"; + OS << " }\n"; + return OS.str(); + } + public: MCDCRecord(const CounterMappingRegion &Region, TestVectors &&TV, - BoolVector &&Folded, CondIDMap &&PosToID, LineColPairMap &&CondLoc) - : Region(Region), TV(std::move(TV)), Folded(std::move(Folded)), + TestVectors &&NotExecutedTV, BoolVector &&Folded, + CondIDMap &&PosToID, LineColPairMap &&CondLoc) + : Region(Region), TV(std::move(TV)), + NotExecutedTV(std::move(NotExecutedTV)), Folded(std::move(Folded)), PosToID(std::move(PosToID)), CondLoc(std::move(CondLoc)) { findIndependencePairs(); } @@ -481,6 +522,8 @@ public: return Region.getDecisionParams().NumConditions; } unsigned getNumTestVectors() const { return TV.size(); } + unsigned getNumNotExecutedTestVectors() const { return NotExecutedTV.size(); } + bool isCondFolded(unsigned Condition) const { return Folded[false][Condition] || Folded[true][Condition]; } @@ -495,6 +538,11 @@ public: return TV[TestVectorIndex].first[PosToID[Condition]]; } + CondState getNotExecutedTVCondition(unsigned NotExecutedIndex, + unsigned Condition) { + return NotExecutedTV[NotExecutedIndex].first[PosToID[Condition]]; + } + /// Return the number of True and False decisions for all executed test /// vectors. std::pair getDecisions() const { @@ -510,6 +558,10 @@ public: return TV[TestVectorIndex].second; } + CondState getNotExecutedTVResult(unsigned NotExecutedIndex) { + return NotExecutedTV[NotExecutedIndex].second; + } + /// 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 @@ -559,7 +611,7 @@ public: std::string getTestVectorHeaderString() const { std::ostringstream OS; - if (getNumTestVectors() == 0) { + if (getNumTestVectors() == 0 && getNumNotExecutedTestVectors() == 0) { OS << "None.\n"; return OS.str(); } @@ -576,39 +628,16 @@ public: 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 << ", "; - } + const auto &[Vec, Res] = TV[TestVectorIndex]; + return formatTestVectorRow(Vec, Res, TestVectorIndex + 1); + } - // Add result value to the string. - OS << " = "; - if (getTVResult(TestVectorIndex) == MCDC_True) - OS << "T"; - else - OS << "F"; - OS << " }\n"; - - return OS.str(); + std::string getNotExecutedTestVectorString(unsigned NotExecutedIndex) { + assert(NotExecutedIndex < getNumNotExecutedTestVectors() && + "Not-executed test vector index out of bounds!"); + const auto &[Vec, Res] = NotExecutedTV[NotExecutedIndex]; + return formatTestVectorRow(Vec, Res, + getNumTestVectors() + NotExecutedIndex + 1); } std::string getConditionCoverageString(unsigned Condition) { diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp index 8b54374aa0b1..0d9a5a6758f0 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -426,11 +426,11 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder { /// Mapping of calculated MC/DC Independence Pairs for each condition. MCDCRecord::TVPairMap IndependencePairs; - /// Helper for sorting ExecVectors. + /// Helper for sorting ExecVectors / NotExecVectors. struct TVIdxTuple { MCDCRecord::CondState MCDCCond; /// True/False unsigned BIdx; /// Bitmap Index - unsigned Ord; /// Last position on ExecVectors + unsigned Ord; /// Last position in exec / not-exec TVs TVIdxTuple(MCDCRecord::CondState MCDCCond, unsigned BIdx, unsigned Ord) : MCDCCond(MCDCCond), BIdx(BIdx), Ord(Ord) {} @@ -441,12 +441,14 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder { } }; - // Indices for sorted TestVectors; std::vector ExecVectorIdxs; + std::vector NotExecVectorIdxs; /// Actual executed Test Vectors for the boolean expression, based on /// ExecutedTestVectorBitmap. MCDCRecord::TestVectors ExecVectors; + /// Never-executed test vectors + MCDCRecord::TestVectors NotExecVectors; #ifndef NDEBUG DenseSet TVIdxs; @@ -486,17 +488,21 @@ private: assert(TVIdx < SavedNodes[ID].Width); assert(TVIdxs.insert(NextTVIdx).second && "Duplicate TVIdx"); - if (!Bitmap[IsVersion11 - ? DecisionParams.BitmapIdx * CHAR_BIT + TV.getIndex() - : DecisionParams.BitmapIdx - NumTestVectors + NextTVIdx]) - continue; - - ExecVectorIdxs.emplace_back(MCDCCond, NextTVIdx, ExecVectors.size()); - - // Copy the completed test vector to the vector of testvectors. - // The final value (T,F) is equal to the last non-dontcare state on the - // path (in a short-circuiting system). - ExecVectors.push_back({TV, MCDCCond}); + bool Executed = + Bitmap[IsVersion11 + ? DecisionParams.BitmapIdx * CHAR_BIT + TV.getIndex() + : DecisionParams.BitmapIdx - NumTestVectors + NextTVIdx]; + if (Executed) { + ExecVectorIdxs.emplace_back(MCDCCond, NextTVIdx, ExecVectors.size()); + // Copy the completed test vector to the vector of testvectors. + // The final value (T,F) is equal to the last non-dontcare state on the + // path (in a short-circuiting system). + ExecVectors.push_back({TV, MCDCCond}); + } else { + NotExecVectorIdxs.emplace_back(MCDCCond, NextTVIdx, + NotExecVectors.size()); + NotExecVectors.push_back({TV, MCDCCond}); + } } // Reset back to DontCare. @@ -505,7 +511,8 @@ private: /// 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() { + /// Vectors with '0' bit are collected separately for UI. + void findTestVectors() { // Walk the binary decision diagram to enumerate all possible test vectors. // We start at the root node (ID == 0) with all values being DontCare. // `TVIdx` starts with 0 and is in the traversal. @@ -516,10 +523,16 @@ private: "TVIdxs wasn't fulfilled"); llvm::sort(ExecVectorIdxs); - MCDCRecord::TestVectors NewTestVectors; + MCDCRecord::TestVectors NewExec; for (const auto &IdxTuple : ExecVectorIdxs) - NewTestVectors.push_back(std::move(ExecVectors[IdxTuple.Ord])); - ExecVectors = std::move(NewTestVectors); + NewExec.push_back(std::move(ExecVectors[IdxTuple.Ord])); + ExecVectors = std::move(NewExec); + + llvm::sort(NotExecVectorIdxs); + MCDCRecord::TestVectors NewNotExec; + for (const auto &IdxTuple : NotExecVectorIdxs) + NewNotExec.push_back(std::move(NotExecVectors[IdxTuple.Ord])); + NotExecVectors = std::move(NewNotExec); } public: @@ -554,12 +567,13 @@ public: Folded[true][I] = B->Count.isZero(); } - // Using Profile Bitmap from runtime, mark the executed test vectors. - findExecutedTestVectors(); + // Using Profile Bitmap from runtime, mark the test vectors. + findTestVectors(); - // Record Test vectors, executed vectors, and independence pairs. - return MCDCRecord(Region, std::move(ExecVectors), std::move(Folded), - std::move(PosToID), std::move(CondLoc)); + // Record executed vectors, not-executed vectors, and independence pairs. + return MCDCRecord(Region, std::move(ExecVectors), std::move(NotExecVectors), + std::move(Folded), std::move(PosToID), + std::move(CondLoc)); } }; diff --git a/llvm/test/tools/llvm-cov/mcdc-const.test b/llvm/test/tools/llvm-cov/mcdc-const.test index 76eb7cf706d7..1358725dce60 100644 --- a/llvm/test/tools/llvm-cov/mcdc-const.test +++ b/llvm/test/tools/llvm-cov/mcdc-const.test @@ -19,7 +19,9 @@ // CHECKGENERALCASE-NEXT: | Condition C4 --> (12:25) // CHECKGENERALCASE-NEXT: | Condition C5 --> (12:31) // CHECKGENERALCASE-NEXT: | -// CHECKGENERALCASE-NEXT: | Executed MC/DC Test Vectors: +// CHECKGENERALCASE-NEXT: | MC/DC Test Vectors: +// CHECKGENERALCASE-NEXT: | +// CHECKGENERALCASE-NEXT: | Executed: // CHECKGENERALCASE-NEXT: | // CHECKGENERALCASE-NEXT: | C1, C2, C3, C4, C5 Result // CHECKGENERALCASE-NEXT: | 1 { F, C, C, -, C = F } @@ -36,7 +38,6 @@ // 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=.,%S/Inputs | 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=.,%S/Inputs %S/Inputs/mcdc-const-folding.cpp | FileCheck %s -check-prefix=REPORT // CHECKFULLCASE: | 1 { C, - = F } // CHECKFULLCASE: | C1-Pair: constant folded @@ -176,6 +177,148 @@ // CHECKFULLCASE-NEXT: | C3-Pair: not covered // CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% +// RUN: llvm-cov show --show-mcdc --show-mcdc-non-executed-vectors %S/Inputs/mcdc-const-folding.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %s -check-prefix=ALLFULLCASE + +// ALLFULLCASE: | 1 { C, - = F } +// ALLFULLCASE: | C1-Pair: constant folded +// ALLFULLCASE-NEXT: | C2-Pair: not covered +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { F, C = F } +// ALLFULLCASE-NEXT: | 2 { T, C = F } +// ALLFULLCASE: | C1-Pair: not covered +// ALLFULLCASE-NEXT: | C2-Pair: constant folded +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { C, F = F } +// ALLFULLCASE-NEXT: | 2 { C, T = T } +// ALLFULLCASE: | C1-Pair: constant folded +// ALLFULLCASE-NEXT: | C2-Pair: covered: (1,2) +// ALLFULLCASE: | MC/DC Coverage for Decision: 100.00% +// ALLFULLCASE: | 1 { F, C = F } +// ALLFULLCASE-NEXT: | 2 { T, C = T } +// ALLFULLCASE: | C1-Pair: covered: (1,2) +// ALLFULLCASE-NEXT: | C2-Pair: constant folded +// ALLFULLCASE: | MC/DC Coverage for Decision: 100.00% +// ALLFULLCASE: | 1 { C, - = T } +// ALLFULLCASE: | C1-Pair: constant folded +// ALLFULLCASE-NEXT: | C2-Pair: not covered +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { T, C = T } +// ALLFULLCASE-NEXT: | 2 { F, C = T } +// ALLFULLCASE: | C1-Pair: not covered +// ALLFULLCASE-NEXT: | C2-Pair: constant folded +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { C, F = F } +// ALLFULLCASE-NEXT: | 2 { C, T = T } +// ALLFULLCASE: | C1-Pair: constant folded +// ALLFULLCASE-NEXT: | C2-Pair: covered: (1,2) +// ALLFULLCASE: | MC/DC Coverage for Decision: 100.00% +// ALLFULLCASE: | 1 { F, C = F } +// ALLFULLCASE-NEXT: | 2 { T, C = T } +// ALLFULLCASE: | C1-Pair: covered: (1,2) +// ALLFULLCASE-NEXT: | C2-Pair: constant folded +// ALLFULLCASE: | MC/DC Coverage for Decision: 100.00% +// ALLFULLCASE: | 1 { C, -, - = F } +// ALLFULLCASE: | C1-Pair: constant folded +// ALLFULLCASE-NEXT: | C2-Pair: not covered +// ALLFULLCASE-NEXT: | C3-Pair: not covered +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { F, C, - = F } +// ALLFULLCASE-NEXT: | 2 { T, C, - = F } +// ALLFULLCASE: | C1-Pair: not covered +// ALLFULLCASE-NEXT: | C2-Pair: constant folded +// ALLFULLCASE-NEXT: | C3-Pair: not covered +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { C, F, - = F } +// ALLFULLCASE-NEXT: | 2 { C, T, F = F } +// ALLFULLCASE-NEXT: | 3 { C, T, T = T } +// ALLFULLCASE: | C1-Pair: constant folded +// ALLFULLCASE-NEXT: | C2-Pair: covered: (1,3) +// ALLFULLCASE-NEXT: | C3-Pair: covered: (2,3) +// ALLFULLCASE: | MC/DC Coverage for Decision: 100.00% +// ALLFULLCASE: | 1 { F, C, - = F } +// ALLFULLCASE-NEXT: | 2 { T, C, F = F } +// ALLFULLCASE-NEXT: | 3 { T, C, T = T } +// ALLFULLCASE: | C1-Pair: covered: (1,3) +// ALLFULLCASE-NEXT: | C2-Pair: constant folded +// ALLFULLCASE-NEXT: | C3-Pair: covered: (2,3) +// ALLFULLCASE: | MC/DC Coverage for Decision: 100.00% +// ALLFULLCASE: | 1 { C, -, - = T } +// ALLFULLCASE: | C1-Pair: constant folded +// ALLFULLCASE-NEXT: | C2-Pair: not covered +// ALLFULLCASE-NEXT: | C3-Pair: not covered +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { T, C, - = T } +// ALLFULLCASE-NEXT: | 2 { F, C, - = T } +// ALLFULLCASE: | C1-Pair: not covered +// ALLFULLCASE-NEXT: | C2-Pair: constant folded +// ALLFULLCASE-NEXT: | C3-Pair: not covered +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { C, T, - = T } +// ALLFULLCASE-NEXT: | 2 { C, F, T = T } +// ALLFULLCASE: | C1-Pair: constant folded +// ALLFULLCASE-NEXT: | C2-Pair: not covered +// ALLFULLCASE-NEXT: | C3-Pair: not covered +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { T, C, - = T } +// ALLFULLCASE-NEXT: | 2 { F, C, T = T } +// ALLFULLCASE: | C1-Pair: not covered +// ALLFULLCASE-NEXT: | C2-Pair: constant folded +// ALLFULLCASE-NEXT: | C3-Pair: not covered +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { F, -, C = F } +// ALLFULLCASE-NEXT: | 2 { T, F, C = F } +// ALLFULLCASE-NEXT: | 3 { T, T, C = F } +// ALLFULLCASE: | C1-Pair: not covered +// ALLFULLCASE-NEXT: | C2-Pair: not covered +// ALLFULLCASE-NEXT: | C3-Pair: constant folded +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { F, C, - = F } +// ALLFULLCASE-NEXT: | 2 { T, C, - = F } +// ALLFULLCASE: | C1-Pair: not covered +// ALLFULLCASE-NEXT: | C2-Pair: constant folded +// ALLFULLCASE-NEXT: | C3-Pair: not covered +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { F, -, C = F } +// ALLFULLCASE-NEXT: | 2 { T, F, C = F } +// ALLFULLCASE-NEXT: | 3 { T, T, C = T } +// ALLFULLCASE: | C1-Pair: covered: (1,3) +// ALLFULLCASE-NEXT: | C2-Pair: covered: (2,3) +// ALLFULLCASE-NEXT: | C3-Pair: constant folded +// ALLFULLCASE: | MC/DC Coverage for Decision: 100.00% +// ALLFULLCASE: | 1 { F, C, - = F } +// ALLFULLCASE-NEXT: | 2 { T, C, F = F } +// ALLFULLCASE-NEXT: | 3 { T, C, T = T } +// ALLFULLCASE: | C1-Pair: covered: (1,3) +// ALLFULLCASE-NEXT: | C2-Pair: constant folded +// ALLFULLCASE-NEXT: | C3-Pair: covered: (2,3) +// ALLFULLCASE: | MC/DC Coverage for Decision: 100.00% +// ALLFULLCASE: | 1 { T, -, C = T } +// ALLFULLCASE-NEXT: | 2 { F, T, C = T } +// ALLFULLCASE: | C1-Pair: not covered +// ALLFULLCASE-NEXT: | C2-Pair: not covered +// ALLFULLCASE-NEXT: | C3-Pair: constant folded +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { T, C, - = T } +// ALLFULLCASE-NEXT: | 2 { F, C, - = T } +// ALLFULLCASE: | C1-Pair: not covered +// ALLFULLCASE-NEXT: | C2-Pair: constant folded +// ALLFULLCASE-NEXT: | C3-Pair: not covered +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { T, -, C = T } +// ALLFULLCASE-NEXT: | 2 { F, T, C = T } +// ALLFULLCASE: | C1-Pair: not covered +// ALLFULLCASE-NEXT: | C2-Pair: not covered +// ALLFULLCASE-NEXT: | C3-Pair: constant folded +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% +// ALLFULLCASE: | 1 { T, C, - = T } +// ALLFULLCASE-NEXT: | 2 { F, C, T = T } +// ALLFULLCASE: | C1-Pair: not covered +// ALLFULLCASE-NEXT: | C2-Pair: constant folded +// ALLFULLCASE-NEXT: | C3-Pair: not covered +// ALLFULLCASE: | MC/DC Coverage for Decision: 0.00% + +// RUN: llvm-cov report --show-mcdc-summary %S/Inputs/mcdc-const-folding.o -instr-profile %t.profdata -show-functions -path-equivalence=.,%S/Inputs %S/Inputs/mcdc-const-folding.cpp | FileCheck %s -check-prefix=REPORT + // REPORT: _Z5case0b {{.*}} 1 1 0.00% // REPORT-NEXT: _Z5case1b {{.*}} 1 1 0.00% // REPORT-NEXT: _Z5case2b {{.*}} 1 0 100.00% diff --git a/llvm/test/tools/llvm-cov/mcdc-export-json.test b/llvm/test/tools/llvm-cov/mcdc-export-json.test index 3ff563955c99..e1d0601ba6ab 100644 --- a/llvm/test/tools/llvm-cov/mcdc-export-json.test +++ b/llvm/test/tools/llvm-cov/mcdc-export-json.test @@ -1,11 +1,19 @@ // 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 +// RUN: llvm-cov export --format=text %S/Inputs/mcdc-general.o -instr-profile %t.profdata | FileCheck %s --check-prefix=DEFAULT -// CHECK: 12,7,12,27,2,4,0,0,5,[true,true,true,true],[{"conditions":[false,null,false,null],"executed":true,"result":false},{"conditions":[true,false,false,null],"executed":true,"result":false},{"conditions":[false,null,true,false],"executed":true,"result":false},{"conditions":[true,false,true,false],"executed":true,"result":false},{"conditions":[true,false,true,true],"executed":true,"result":true},{"conditions":[true,true,null,null],"executed":true,"result":true}] -// CHECK: 15,7,15,13,1,2,0,0,5,[true,true],[{"conditions":[false,null],"executed":true,"result":false},{"conditions":[true,false],"executed":true,"result":false},{"conditions":[true,true],"executed":true,"result":true}] -// CHECK: 15,19,15,25,1,1,0,0,5,[true,false],[{"conditions":[false,null],"executed":true,"result":false},{"conditions":[true,true],"executed":true,"result":true}] -// CHECK: 18,7,19,15,1,3,0,0,5,[true,true,false,true],[{"conditions":[false,null,null,null],"executed":true,"result":false},{"conditions":[true,false,null,null],"executed":true,"result":false},{"conditions":[true,true,true,false],"executed":true,"result":false},{"conditions":[true,true,true,true],"executed":true,"result":true}] -// CHECK: "mcdc":{"count":12,"covered":10,"notcovered":2,"percent":83.333333333333343} +// DEFAULT: 12,7,12,27,2,4,0,0,5,[true,true,true,true],[{"conditions":[false,null,false,null],"executed":true,"result":false},{"conditions":[true,false,false,null],"executed":true,"result":false},{"conditions":[false,null,true,false],"executed":true,"result":false},{"conditions":[true,false,true,false],"executed":true,"result":false},{"conditions":[true,false,true,true],"executed":true,"result":true},{"conditions":[true,true,null,null],"executed":true,"result":true}] +// DEFAULT: 15,7,15,13,1,2,0,0,5,[true,true],[{"conditions":[false,null],"executed":true,"result":false},{"conditions":[true,false],"executed":true,"result":false},{"conditions":[true,true],"executed":true,"result":true}] +// DEFAULT: 15,19,15,25,1,1,0,0,5,[true,false],[{"conditions":[false,null],"executed":true,"result":false},{"conditions":[true,true],"executed":true,"result":true}] +// DEFAULT: 18,7,19,15,1,3,0,0,5,[true,true,false,true],[{"conditions":[false,null,null,null],"executed":true,"result":false},{"conditions":[true,false,null,null],"executed":true,"result":false},{"conditions":[true,true,true,false],"executed":true,"result":false},{"conditions":[true,true,true,true],"executed":true,"result":true}] +// DEFAULT: "mcdc":{"count":12,"covered":10,"notcovered":2,"percent":83.333333333333343} + +// RUN: llvm-cov export --format=text --show-mcdc-non-executed-vectors %S/Inputs/mcdc-general.o -instr-profile %t.profdata | FileCheck %s --check-prefix=ALL + +// ALL: 12,7,12,27,2,4,0,0,5,[true,true,true,true],[{"conditions":[false,null,false,null],"executed":true,"result":false},{"conditions":[true,false,false,null],"executed":true,"result":false},{"conditions":[false,null,true,false],"executed":true,"result":false},{"conditions":[true,false,true,false],"executed":true,"result":false},{"conditions":[true,false,true,true],"executed":true,"result":true},{"conditions":[true,true,null,null],"executed":true,"result":true},{"conditions":[false,null,true,true],"executed":false,"result":true}] +// ALL: 15,7,15,13,1,2,0,0,5,[true,true],[{"conditions":[false,null],"executed":true,"result":false},{"conditions":[true,false],"executed":true,"result":false},{"conditions":[true,true],"executed":true,"result":true}] +// ALL: 15,19,15,25,1,1,0,0,5,[true,false],[{"conditions":[false,null],"executed":true,"result":false},{"conditions":[true,true],"executed":true,"result":true},{"conditions":[true,false],"executed":false,"result":false}] +// ALL: 18,7,19,15,1,3,0,0,5,[true,true,false,true],[{"conditions":[false,null,null,null],"executed":true,"result":false},{"conditions":[true,false,null,null],"executed":true,"result":false},{"conditions":[true,true,true,false],"executed":true,"result":false},{"conditions":[true,true,true,true],"executed":true,"result":true},{"conditions":[true,true,false,null],"executed":false,"result":false}] +// ALL: "mcdc":{"count":12,"covered":10,"notcovered":2,"percent":83.333333333333343} Instructions for regenerating the test: @@ -13,6 +21,6 @@ Instructions for regenerating the test: 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 + -fcoverage-mapping /tmp/mcdc-general.cpp -o /tmp/mcdc-general.o mv /tmp/mcdc-general.o %S/Inputs diff --git a/llvm/test/tools/llvm-cov/mcdc-general-none.test b/llvm/test/tools/llvm-cov/mcdc-general-none.test index b57b35d49c8c..f2013a17964d 100644 --- a/llvm/test/tools/llvm-cov/mcdc-general-none.test +++ b/llvm/test/tools/llvm-cov/mcdc-general-none.test @@ -15,9 +15,11 @@ // CHECK-NEXT: | Condition C3 --> (12:20) // CHECK-NEXT: | Condition C4 --> (12:25) // CHECK-NEXT: | -// CHECK-NEXT: | Executed MC/DC Test Vectors: +// CHECK-NEXT: | MC/DC Test Vectors: // CHECK-NEXT: | -// CHECK-NEXT: | None. +// CHECK-NEXT: | Executed: +// CHECK-NEXT: | +// CHECK-NEXT: | None. // CHECK-NEXT: | // CHECK-NEXT: | C1-Pair: not covered // CHECK-NEXT: | C2-Pair: not covered @@ -27,6 +29,124 @@ // CHECK-NEXT: | // CHECK-NEXT: ------------------ +// RUN: llvm-cov show --show-mcdc --show-mcdc-non-executed-vectors %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %s -check-prefix=ALL + +// ALL: test(bool + +// ALL: ------------------ +// ALL-NEXT: |---> MC/DC Decision Region (12:7) to (12:27) +// ALL-NEXT: | +// ALL-NEXT: | Number of Conditions: 4 +// ALL-NEXT: | Condition C1 --> (12:8) +// ALL-NEXT: | Condition C2 --> (12:13) +// ALL-NEXT: | Condition C3 --> (12:20) +// ALL-NEXT: | Condition C4 --> (12:25) +// ALL-NEXT: | +// ALL-NEXT: | MC/DC Test Vectors: +// ALL-NEXT: | +// ALL-NEXT: | Executed: +// ALL-NEXT: | +// ALL-NEXT: | None. +// ALL-NEXT: | +// ALL-NEXT: | Not executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2, C3, C4 Result +// ALL-NEXT: | 1 { F, -, F, - = F } +// ALL-NEXT: | 2 { T, F, F, - = F } +// ALL-NEXT: | 3 { F, -, T, F = F } +// ALL-NEXT: | 4 { T, F, T, F = F } +// ALL-NEXT: | 5 { F, -, T, T = T } +// ALL-NEXT: | 6 { T, F, T, T = T } +// ALL-NEXT: | 7 { T, T, -, - = T } +// ALL-NEXT: | +// ALL-NEXT: | C1-Pair: not covered +// ALL-NEXT: | C2-Pair: not covered +// ALL-NEXT: | C3-Pair: not covered +// ALL-NEXT: | C4-Pair: not covered +// ALL-NEXT: | MC/DC Coverage for Decision: 0.00% +// ALL-NEXT: | +// ALL-NEXT: ------------------ + +// ALL: ------------------ +// ALL-NEXT: |---> MC/DC Decision Region (15:7) to (15:13) +// ALL-NEXT: | +// ALL-NEXT: | Number of Conditions: 2 +// ALL-NEXT: | Condition C1 --> (15:7) +// ALL-NEXT: | Condition C2 --> (15:12) +// ALL-NEXT: | +// ALL-NEXT: | MC/DC Test Vectors: +// ALL-NEXT: | +// ALL-NEXT: | Executed: +// ALL-NEXT: | +// ALL-NEXT: | None. +// ALL-NEXT: | +// ALL-NEXT: | Not executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2 Result +// ALL-NEXT: | 1 { F, - = F } +// ALL-NEXT: | 2 { T, F = F } +// ALL-NEXT: | 3 { T, T = T } +// ALL-NEXT: | +// ALL-NEXT: | C1-Pair: not covered +// ALL-NEXT: | C2-Pair: not covered +// ALL-NEXT: | MC/DC Coverage for Decision: 0.00% +// ALL-NEXT: | +// ALL-NEXT: |---> MC/DC Decision Region (15:19) to (15:25) +// ALL-NEXT: | +// ALL-NEXT: | Number of Conditions: 2 +// ALL-NEXT: | Condition C1 --> (15:19) +// ALL-NEXT: | Condition C2 --> (15:24) +// ALL-NEXT: | +// ALL-NEXT: | MC/DC Test Vectors: +// ALL-NEXT: | +// ALL-NEXT: | Executed: +// ALL-NEXT: | +// ALL-NEXT: | None. +// ALL-NEXT: | +// ALL-NEXT: | Not executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2 Result +// ALL-NEXT: | 1 { F, - = F } +// ALL-NEXT: | 2 { T, F = F } +// ALL-NEXT: | 3 { T, T = T } +// ALL-NEXT: | +// ALL-NEXT: | C1-Pair: not covered +// ALL-NEXT: | C2-Pair: not covered +// ALL-NEXT: | MC/DC Coverage for Decision: 0.00% +// ALL-NEXT: | +// ALL-NEXT: ------------------ + +// ALL: ------------------ +// ALL-NEXT: |---> MC/DC Decision Region (18:7) to (19:15) +// ALL-NEXT: | +// ALL-NEXT: | Number of Conditions: 4 +// ALL-NEXT: | Condition C1 --> (18:8) +// ALL-NEXT: | Condition C2 --> (18:13) +// ALL-NEXT: | Condition C3 --> (19:8) +// ALL-NEXT: | Condition C4 --> (19:13) +// ALL-NEXT: | +// ALL-NEXT: | MC/DC Test Vectors: +// ALL-NEXT: | +// ALL-NEXT: | Executed: +// ALL-NEXT: | +// ALL-NEXT: | None. +// ALL-NEXT: | +// ALL-NEXT: | Not executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2, C3, C4 Result +// ALL-NEXT: | 1 { F, -, -, - = F } +// ALL-NEXT: | 2 { T, F, -, - = F } +// ALL-NEXT: | 3 { T, T, F, - = F } +// ALL-NEXT: | 4 { T, T, T, F = F } +// ALL-NEXT: | 5 { T, T, T, T = T } +// ALL-NEXT: | +// ALL-NEXT: | C1-Pair: not covered +// ALL-NEXT: | C2-Pair: not covered +// ALL-NEXT: | C3-Pair: not covered +// ALL-NEXT: | C4-Pair: not covered +// ALL-NEXT: | MC/DC Coverage for Decision: 0.00% +// ALL-NEXT: | +// ALL-NEXT: ------------------ // Turn off MC/DC visualization. // RUN: llvm-cov show %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %s -check-prefix=NOMCDC diff --git a/llvm/test/tools/llvm-cov/mcdc-general.test b/llvm/test/tools/llvm-cov/mcdc-general.test index 1835af9a4c6b..7f2f449c197b 100644 --- a/llvm/test/tools/llvm-cov/mcdc-general.test +++ b/llvm/test/tools/llvm-cov/mcdc-general.test @@ -2,6 +2,7 @@ // 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=.,%S/Inputs | FileCheck %s +// RUN: llvm-cov show --show-mcdc --show-mcdc-non-executed-vectors %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %s -check-prefix=ALL // RUN: llvm-cov report --show-mcdc-summary %S/Inputs/mcdc-general.o -instr-profile %t.profdata -show-functions -path-equivalence=.,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=REPORT // CHECK: test(bool @@ -15,7 +16,9 @@ // CHECK-NEXT: | Condition C3 --> (12:20) // CHECK-NEXT: | Condition C4 --> (12:25) // CHECK-NEXT: | -// CHECK-NEXT: | Executed MC/DC Test Vectors: +// CHECK-NEXT: | MC/DC Test Vectors: +// CHECK-NEXT: | +// CHECK-NEXT: | Executed: // CHECK-NEXT: | // CHECK-NEXT: | C1, C2, C3, C4 Result // CHECK-NEXT: | 1 { F, -, F, - = F } @@ -40,7 +43,9 @@ // CHECK-NEXT: | Condition C1 --> (15:7) // CHECK-NEXT: | Condition C2 --> (15:12) // CHECK-NEXT: | -// CHECK-NEXT: | Executed MC/DC Test Vectors: +// CHECK-NEXT: | MC/DC Test Vectors: +// CHECK-NEXT: | +// CHECK-NEXT: | Executed: // CHECK-NEXT: | // CHECK-NEXT: | C1, C2 Result // CHECK-NEXT: | 1 { F, - = F } @@ -57,7 +62,9 @@ // CHECK-NEXT: | Condition C1 --> (15:19) // CHECK-NEXT: | Condition C2 --> (15:24) // CHECK-NEXT: | -// CHECK-NEXT: | Executed MC/DC Test Vectors: +// CHECK-NEXT: | MC/DC Test Vectors: +// CHECK-NEXT: | +// CHECK-NEXT: | Executed: // CHECK-NEXT: | // CHECK-NEXT: | C1, C2 Result // CHECK-NEXT: | 1 { F, - = F } @@ -78,7 +85,9 @@ // CHECK-NEXT: | Condition C3 --> (19:8) // CHECK-NEXT: | Condition C4 --> (19:13) // CHECK-NEXT: | -// CHECK-NEXT: | Executed MC/DC Test Vectors: +// CHECK-NEXT: | MC/DC Test Vectors: +// CHECK-NEXT: | +// CHECK-NEXT: | Executed: // CHECK-NEXT: | // CHECK-NEXT: | C1, C2, C3, C4 Result // CHECK-NEXT: | 1 { F, -, -, - = F } @@ -94,6 +103,125 @@ // CHECK-NEXT: | // CHECK-NEXT: ------------------ +// Same as CHECK above, but with --show-mcdc-non-executed-vectors (Not executed section after each decision). + +// ALL: test(bool + +// ALL: ------------------ +// ALL-NEXT: |---> MC/DC Decision Region (12:7) to (12:27) +// ALL-NEXT: | +// ALL-NEXT: | Number of Conditions: 4 +// ALL-NEXT: | Condition C1 --> (12:8) +// ALL-NEXT: | Condition C2 --> (12:13) +// ALL-NEXT: | Condition C3 --> (12:20) +// ALL-NEXT: | Condition C4 --> (12:25) +// ALL-NEXT: | +// ALL-NEXT: | MC/DC Test Vectors: +// ALL-NEXT: | +// ALL-NEXT: | Executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2, C3, C4 Result +// ALL-NEXT: | 1 { F, -, F, - = F } +// ALL-NEXT: | 2 { T, F, F, - = F } +// ALL-NEXT: | 3 { F, -, T, F = F } +// ALL-NEXT: | 4 { T, F, T, F = F } +// ALL-NEXT: | 5 { T, F, T, T = T } +// ALL-NEXT: | 6 { T, T, -, - = T } +// ALL-NEXT: | +// ALL-NEXT: | Not executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2, C3, C4 Result +// ALL-NEXT: | 7 { F, -, T, T = T } +// ALL-NEXT: | +// ALL-NEXT: | C1-Pair: covered: (1,6) +// ALL-NEXT: | C2-Pair: covered: (2,6) +// ALL-NEXT: | C3-Pair: covered: (2,5) +// ALL-NEXT: | C4-Pair: covered: (4,5) +// ALL-NEXT: | MC/DC Coverage for Decision: 100.00% +// ALL-NEXT: | +// ALL-NEXT: ------------------ + +// ALL: ------------------ +// ALL-NEXT: |---> MC/DC Decision Region (15:7) to (15:13) +// ALL-NEXT: | +// ALL-NEXT: | Number of Conditions: 2 +// ALL-NEXT: | Condition C1 --> (15:7) +// ALL-NEXT: | Condition C2 --> (15:12) +// ALL-NEXT: | +// ALL-NEXT: | MC/DC Test Vectors: +// ALL-NEXT: | +// ALL-NEXT: | Executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2 Result +// ALL-NEXT: | 1 { F, - = F } +// ALL-NEXT: | 2 { T, F = F } +// ALL-NEXT: | 3 { T, T = T } +// ALL-NEXT: | +// ALL-NEXT: | Not executed: +// ALL-NEXT: | +// ALL-NEXT: | None. +// ALL-NEXT: | +// ALL-NEXT: | C1-Pair: covered: (1,3) +// ALL-NEXT: | C2-Pair: covered: (2,3) +// ALL-NEXT: | MC/DC Coverage for Decision: 100.00% +// ALL-NEXT: | +// ALL-NEXT: |---> MC/DC Decision Region (15:19) to (15:25) +// ALL-NEXT: | +// ALL-NEXT: | Number of Conditions: 2 +// ALL-NEXT: | Condition C1 --> (15:19) +// ALL-NEXT: | Condition C2 --> (15:24) +// ALL-NEXT: | +// ALL-NEXT: | MC/DC Test Vectors: +// ALL-NEXT: | +// ALL-NEXT: | Executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2 Result +// ALL-NEXT: | 1 { F, - = F } +// ALL-NEXT: | 2 { T, T = T } +// ALL-NEXT: | +// ALL-NEXT: | Not executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2 Result +// ALL-NEXT: | 3 { T, F = F } +// ALL-NEXT: | +// ALL-NEXT: | C1-Pair: covered: (1,2) +// ALL-NEXT: | C2-Pair: not covered +// ALL-NEXT: | MC/DC Coverage for Decision: 50.00% +// ALL-NEXT: | +// ALL-NEXT: ------------------ + +// ALL: ------------------ +// ALL-NEXT: |---> MC/DC Decision Region (18:7) to (19:15) +// ALL-NEXT: | +// ALL-NEXT: | Number of Conditions: 4 +// ALL-NEXT: | Condition C1 --> (18:8) +// ALL-NEXT: | Condition C2 --> (18:13) +// ALL-NEXT: | Condition C3 --> (19:8) +// ALL-NEXT: | Condition C4 --> (19:13) +// ALL-NEXT: | +// ALL-NEXT: | MC/DC Test Vectors: +// ALL-NEXT: | +// ALL-NEXT: | Executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2, C3, C4 Result +// ALL-NEXT: | 1 { F, -, -, - = F } +// ALL-NEXT: | 2 { T, F, -, - = F } +// ALL-NEXT: | 3 { T, T, T, F = F } +// ALL-NEXT: | 4 { T, T, T, T = T } +// ALL-NEXT: | +// ALL-NEXT: | Not executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2, C3, C4 Result +// ALL-NEXT: | 5 { T, T, F, - = F } +// ALL-NEXT: | +// ALL-NEXT: | C1-Pair: covered: (1,4) +// ALL-NEXT: | C2-Pair: covered: (2,4) +// ALL-NEXT: | C3-Pair: not covered +// ALL-NEXT: | C4-Pair: covered: (3,4) +// ALL-NEXT: | MC/DC Coverage for Decision: 75.00% +// ALL-NEXT: | +// ALL-NEXT: ------------------ + // Turn off MC/DC visualization. // RUN: llvm-cov show %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %s -check-prefix=NOMCDC // NOMCDC-NOT: MC/DC Decision Region diff --git a/llvm/test/tools/llvm-cov/mcdc-macro.test b/llvm/test/tools/llvm-cov/mcdc-macro.test index d68f4aeb67bc..e55d7e56c3f0 100644 --- a/llvm/test/tools/llvm-cov/mcdc-macro.test +++ b/llvm/test/tools/llvm-cov/mcdc-macro.test @@ -2,6 +2,7 @@ // RUN: llvm-profdata merge %S/Inputs/mcdc-macro.proftext -o %t.profdata // RUN: llvm-cov show --show-expansions --show-branches=count --show-mcdc %S/Inputs/mcdc-macro.o -instr-profile %t.profdata --compilation-dir=%S/Inputs | FileCheck %s +// RUN: llvm-cov show --show-expansions --show-branches=count --show-mcdc --show-mcdc-non-executed-vectors %S/Inputs/mcdc-macro.o -instr-profile %t.profdata --compilation-dir=%S/Inputs | FileCheck %s -check-prefix=ALL // CHECK: | | | Branch (2:11): [Folded - Ignored] // CHECK: | | | Branch (3:11): [True: 1, False: 0] @@ -18,7 +19,9 @@ // CHECK-NEXT: | Condition C4 --> (3:23) // CHECK-NEXT: | Condition C5 --> (9:22) // CHECK-NEXT: | -// CHECK-NEXT: | Executed MC/DC Test Vectors: +// CHECK-NEXT: | MC/DC Test Vectors: +// CHECK-NEXT: | +// CHECK-NEXT: | Executed: // CHECK-NEXT: | // CHECK-NEXT: | C1, C2, C3, C4, C5 Result // CHECK-NEXT: | 1 { T, C, T, T, - = T } @@ -41,7 +44,9 @@ // CHECK-NEXT: | Condition C1 --> (11:7) // CHECK-NEXT: | Condition C2 --> (2:11) // CHECK-NEXT: | -// CHECK-NEXT: | Executed MC/DC Test Vectors: +// CHECK-NEXT: | MC/DC Test Vectors: +// CHECK-NEXT: | +// CHECK-NEXT: | Executed: // CHECK-NEXT: | // CHECK-NEXT: | C1, C2 Result // CHECK-NEXT: | 1 { T, C = T } @@ -69,7 +74,9 @@ // CHECK-NEXT: | Condition C5 --> (3:11) // CHECK-NEXT: | Condition C6 --> (3:23) // CHECK-NEXT: | -// CHECK-NEXT: | Executed MC/DC Test Vectors: +// CHECK-NEXT: | MC/DC Test Vectors: +// CHECK-NEXT: | +// CHECK-NEXT: | Executed: // CHECK-NEXT: | // CHECK-NEXT: | C1, C2, C3, C4, C5, C6 Result // CHECK-NEXT: | 1 { T, T, T, C, -, - = T } @@ -84,6 +91,129 @@ // CHECK-NEXT: | // CHECK-NEXT: ------------------ +// Same as CHECK above, but with --show-mcdc-non-executed-vectors (Not executed section after each decision). + +// ALL: | | | Branch (2:11): [Folded - Ignored] +// ALL: | | | Branch (3:11): [True: 1, False: 0] +// ALL: | | | Branch (3:23): [True: 1, False: 0] +// ALL: | Branch (9:7): [True: 1, False: 0] +// ALL-NEXT: | Branch (9:22): [True: 0, False: 0] +// ALL-NEXT: ------------------ +// ALL-NEXT: |---> MC/DC Decision Region (9:7) to (9:23) +// ALL-NEXT: | +// ALL-NEXT: | Number of Conditions: 5 +// ALL-NEXT: | Condition C1 --> (9:7) +// ALL-NEXT: | Condition C2 --> (2:11) +// ALL-NEXT: | Condition C3 --> (3:11) +// ALL-NEXT: | Condition C4 --> (3:23) +// ALL-NEXT: | Condition C5 --> (9:22) +// ALL-NEXT: | +// ALL-NEXT: | MC/DC Test Vectors: +// ALL-NEXT: | +// ALL-NEXT: | Executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2, C3, C4, C5 Result +// ALL-NEXT: | 1 { T, C, T, T, - = T } +// ALL-NEXT: | +// ALL-NEXT: | Not executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2, C3, C4, C5 Result +// ALL-NEXT: | 2 { F, C, -, -, F = F } +// ALL-NEXT: | 3 { T, C, -, -, F = F } +// ALL-NEXT: | 4 { T, C, F, -, F = F } +// ALL-NEXT: | 5 { T, C, T, F, F = F } +// ALL-NEXT: | 6 { F, C, -, -, T = T } +// ALL-NEXT: | 7 { T, C, -, -, T = T } +// ALL-NEXT: | 8 { T, C, F, -, T = T } +// ALL-NEXT: | 9 { T, C, T, F, T = T } +// ALL-NEXT: | +// ALL-NEXT: | C1-Pair: not covered +// ALL-NEXT: | C2-Pair: constant folded +// ALL-NEXT: | C3-Pair: not covered +// ALL-NEXT: | C4-Pair: not covered +// ALL-NEXT: | C5-Pair: not covered +// ALL-NEXT: | MC/DC Coverage for Decision: 0.00% +// ALL-NEXT: | +// ALL-NEXT: ------------------ + +// ALL: | | | Branch (2:11): [Folded - Ignored] +// ALL: | Branch (11:7): [True: 1, False: 0] +// ALL-NEXT: ------------------ +// ALL-NEXT: |---> MC/DC Decision Region (11:7) to (11:13) +// ALL-NEXT: | +// ALL-NEXT: | Number of Conditions: 2 +// ALL-NEXT: | Condition C1 --> (11:7) +// ALL-NEXT: | Condition C2 --> (2:11) +// ALL-NEXT: | +// ALL-NEXT: | MC/DC Test Vectors: +// ALL-NEXT: | +// ALL-NEXT: | Executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2 Result +// ALL-NEXT: | 1 { T, C = T } +// ALL-NEXT: | +// ALL-NEXT: | Not executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2 Result +// ALL-NEXT: | 2 { F, C = F } +// ALL-NEXT: | 3 { T, C = F } +// ALL-NEXT: | +// ALL-NEXT: | C1-Pair: not covered +// ALL-NEXT: | C2-Pair: constant folded +// ALL-NEXT: | MC/DC Coverage for Decision: 0.00% +// ALL-NEXT: | +// ALL-NEXT: ------------------ + +// ALL: | | | Branch (1:11): [True: 1, False: 0] +// ALL: | | | Branch (2:11): [Folded - Ignored] +// ALL: | | | | | Branch (3:11): [True: 0, False: 0] +// ALL: | | | | | Branch (3:23): [True: 0, False: 0] +// ALL: | Branch (13:7): [True: 1, False: 0] +// ALL-NEXT: | Branch (13:13): [True: 1, False: 0] +// ALL-NEXT: ------------------ +// ALL-NEXT: |---> MC/DC Decision Region (13:7) to (13:32) +// ALL-NEXT: | +// ALL-NEXT: | Number of Conditions: 6 +// ALL-NEXT: | Condition C1 --> (13:7) +// ALL-NEXT: | Condition C2 --> (13:13) +// ALL-NEXT: | Condition C3 --> (1:11) +// ALL-NEXT: | Condition C4 --> (2:11) +// ALL-NEXT: | Condition C5 --> (3:11) +// ALL-NEXT: | Condition C6 --> (3:23) +// ALL-NEXT: | +// ALL-NEXT: | MC/DC Test Vectors: +// ALL-NEXT: | +// ALL-NEXT: | Executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2, C3, C4, C5, C6 Result +// ALL-NEXT: | 1 { T, T, T, C, -, - = T } +// ALL-NEXT: | +// ALL-NEXT: | Not executed: +// ALL-NEXT: | +// ALL-NEXT: | C1, C2, C3, C4, C5, C6 Result +// ALL-NEXT: | 2 { F, -, -, C, -, - = F } +// ALL-NEXT: | 3 { T, F, -, C, -, - = F } +// ALL-NEXT: | 4 { T, T, F, C, -, - = F } +// ALL-NEXT: | 5 { F, -, -, C, F, - = F } +// ALL-NEXT: | 6 { T, F, -, C, F, - = F } +// ALL-NEXT: | 7 { T, T, F, C, F, - = F } +// ALL-NEXT: | 8 { F, -, -, C, T, F = F } +// ALL-NEXT: | 9 { T, F, -, C, T, F = F } +// ALL-NEXT: | 10 { T, T, F, C, T, F = F } +// ALL-NEXT: | 11 { F, -, -, C, T, T = T } +// ALL-NEXT: | 12 { T, F, -, C, T, T = T } +// ALL-NEXT: | 13 { T, T, F, C, T, T = T } +// ALL-NEXT: | +// ALL-NEXT: | C1-Pair: not covered +// ALL-NEXT: | C2-Pair: not covered +// ALL-NEXT: | C3-Pair: not covered +// ALL-NEXT: | C4-Pair: constant folded +// ALL-NEXT: | C5-Pair: not covered +// ALL-NEXT: | C6-Pair: not covered +// ALL-NEXT: | MC/DC Coverage for Decision: 0.00% +// ALL-NEXT: | +// ALL-NEXT: ------------------ + Instructions for regenerating the test: cd %S/Inputs # Or copy mcdc-macro.c into the working directory diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp index 4be6d8d9cdaf..1196faa1acef 100644 --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -1024,6 +1024,11 @@ int CodeCoverageTool::doShow(int argc, const char **argv, cl::desc("Show the MCDC Coverage for each applicable boolean expression"), cl::cat(ViewCategory)); + cl::opt ShowMCDCNonExecutedVectors( + "show-mcdc-non-executed-vectors", cl::Optional, + cl::desc("Show MC/DC test vectors that were not executed"), + cl::cat(ViewCategory)); + cl::opt ShowBestLineRegionsCounts( "show-line-counts-or-regions", cl::Optional, cl::desc("Show the execution counts for each line, or the execution " @@ -1130,6 +1135,7 @@ int CodeCoverageTool::doShow(int argc, const char **argv, ViewOpts.ShowBranchCounts = ShowBranches == CoverageViewOptions::BranchOutputType::Count; ViewOpts.ShowMCDC = ShowMCDC; + ViewOpts.ShowMCDCNonExecutedVectors = ShowMCDCNonExecutedVectors; ViewOpts.ShowBranchPercents = ShowBranches == CoverageViewOptions::BranchOutputType::Percent; ViewOpts.ShowFunctionInstantiations = ShowInstantiations; @@ -1321,6 +1327,12 @@ int CodeCoverageTool::doExport(int argc, const char **argv, cl::desc("Unify function instantiations"), cl::init(true), cl::cat(ExportCategory)); + cl::opt ShowMCDCNonExecutedVectors( + "show-mcdc-non-executed-vectors", cl::Optional, + cl::desc("Include MC/DC test vectors that were not executed in the " + "export"), + cl::cat(ExportCategory)); + auto Err = commandLineParser(argc, argv); if (Err) return Err; @@ -1329,6 +1341,7 @@ int CodeCoverageTool::doExport(int argc, const char **argv, ViewOpts.SkipFunctions = SkipFunctions; ViewOpts.SkipBranches = SkipBranches; ViewOpts.UnifyFunctionInstantiations = UnifyInstantiations; + ViewOpts.ShowMCDCNonExecutedVectors = ShowMCDCNonExecutedVectors; if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text && ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) { diff --git a/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/llvm/tools/llvm-cov/CoverageExporterJson.cpp index 4c07c0539673..5143760626c5 100644 --- a/llvm/tools/llvm-cov/CoverageExporterJson.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterJson.cpp @@ -22,7 +22,7 @@ // -- 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 and -// list of executed test vectors +// list of test vectors with execution status // -- 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 @@ -121,11 +121,13 @@ json::Value renderCondState(const coverage::MCDCRecord::CondState CondState) { llvm_unreachable("Unknown llvm::coverage::MCDCRecord::CondState enum"); } -json::Array gatherTestVectors(coverage::MCDCRecord &Record) { +json::Array gatherTestVectors(coverage::MCDCRecord &Record, + const CoverageViewOptions &Options) { json::Array TestVectors; unsigned NumConditions = Record.getNumConditions(); - for (unsigned tv = 0; tv < Record.getNumTestVectors(); tv++) { + const bool ShowNonExecutedVectors = Options.ShowMCDCNonExecutedVectors; + for (unsigned tv = 0; tv < Record.getNumTestVectors(); tv++) { json::Array TVConditions; for (unsigned c = 0; c < NumConditions; c++) TVConditions.push_back(renderCondState(Record.getTVCondition(tv, c))); @@ -135,17 +137,31 @@ json::Array gatherTestVectors(coverage::MCDCRecord &Record) { {"result", renderCondState(Record.getTVResult(tv))}, {"conditions", std::move(TVConditions)}})); } + if (ShowNonExecutedVectors) { + for (unsigned tv = 0; tv < Record.getNumNotExecutedTestVectors(); tv++) { + json::Array TVConditions; + for (unsigned c = 0; c < NumConditions; c++) + TVConditions.push_back( + renderCondState(Record.getNotExecutedTVCondition(tv, c))); + + TestVectors.push_back(json::Object( + {{"executed", json::Value(false)}, + {"result", renderCondState(Record.getNotExecutedTVResult(tv))}, + {"conditions", std::move(TVConditions)}})); + } + } return TestVectors; } -json::Array renderMCDCRecord(const coverage::MCDCRecord &Record) { +json::Array renderMCDCRecord(const coverage::MCDCRecord &Record, + const CoverageViewOptions &Options) { const llvm::coverage::CounterMappingRegion &CMR = Record.getDecisionRegion(); const auto [TrueDecisions, FalseDecisions] = Record.getDecisions(); return json::Array( {CMR.LineStart, CMR.ColumnStart, CMR.LineEnd, CMR.ColumnEnd, TrueDecisions, FalseDecisions, CMR.FileID, CMR.ExpandedFileID, int64_t(CMR.Kind), gatherConditions(Record), - gatherTestVectors(const_cast(Record))}); + gatherTestVectors(const_cast(Record), Options)}); } json::Array renderRegions(ArrayRef Regions) { @@ -163,10 +179,11 @@ json::Array renderBranchRegions(ArrayRef Regions) { return RegionArray; } -json::Array renderMCDCRecords(ArrayRef Records) { +json::Array renderMCDCRecords(ArrayRef Records, + const CoverageViewOptions &Options) { json::Array RecordArray; for (auto &Record : Records) - RecordArray.push_back(renderMCDCRecord(Record)); + RecordArray.push_back(renderMCDCRecord(Record, Options)); return RecordArray; } @@ -268,10 +285,11 @@ json::Array renderFileBranches(const coverage::CoverageData &FileCoverage) { return BranchArray; } -json::Array renderFileMCDC(const coverage::CoverageData &FileCoverage) { +json::Array renderFileMCDC(const coverage::CoverageData &FileCoverage, + const CoverageViewOptions &Options) { json::Array MCDCRecordArray; for (const auto &Record : FileCoverage.getMCDCRecords()) - MCDCRecordArray.push_back(renderMCDCRecord(Record)); + MCDCRecordArray.push_back(renderMCDCRecord(Record, Options)); return MCDCRecordArray; } @@ -285,7 +303,7 @@ json::Object renderFile(const coverage::CoverageMapping &Coverage, auto FileCoverage = Coverage.getCoverageForFile(Filename); File["segments"] = renderFileSegments(FileCoverage); File["branches"] = renderFileBranches(FileCoverage); - File["mcdc_records"] = renderFileMCDC(FileCoverage); + File["mcdc_records"] = renderFileMCDC(FileCoverage, Options); if (!Options.SkipExpansions) { File["expansions"] = renderFileExpansions(Coverage, FileCoverage); } @@ -325,16 +343,17 @@ json::Array renderFiles(const coverage::CoverageMapping &Coverage, } json::Array renderFunctions( - const iterator_range &Functions) { + const iterator_range &Functions, + const CoverageViewOptions &Options) { json::Array FunctionArray; for (const auto &F : Functions) - FunctionArray.push_back( - json::Object({{"name", F.Name}, - {"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)}})); + FunctionArray.push_back(json::Object( + {{"name", F.Name}, + {"count", clamp_uint64_to_int64(F.ExecutionCount)}, + {"regions", renderRegions(F.CountedRegions)}, + {"branches", renderBranchRegions(F.CountedBranchRegions)}, + {"mcdc_records", renderMCDCRecords(F.MCDCRecords, Options)}, + {"filenames", json::Array(F.Filenames)}})); return FunctionArray; } @@ -368,7 +387,8 @@ void CoverageExporterJson::renderRoot(ArrayRef SourceFiles) { {{"files", std::move(Files)}, {"totals", renderSummary(Totals)}}); // Skip functions-level information if necessary. if (!Options.ExportSummaryOnly && !Options.SkipFunctions) - Export["functions"] = renderFunctions(Coverage.getCoveredFunctions()); + Export["functions"] = + renderFunctions(Coverage.getCoveredFunctions(), Options); auto ExportArray = json::Array({std::move(Export)}); diff --git a/llvm/tools/llvm-cov/CoverageViewOptions.h b/llvm/tools/llvm-cov/CoverageViewOptions.h index 7ac38d76a7e4..4826d4adb53b 100644 --- a/llvm/tools/llvm-cov/CoverageViewOptions.h +++ b/llvm/tools/llvm-cov/CoverageViewOptions.h @@ -31,6 +31,7 @@ struct CoverageViewOptions { bool ShowLineStats; bool ShowRegionMarkers; bool ShowMCDC; + bool ShowMCDCNonExecutedVectors = false; bool ShowBranchCounts; bool ShowBranchPercents; bool ShowExpandedRegions; diff --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp index bdef0994d184..5c31fe5ab1b0 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp @@ -1152,6 +1152,8 @@ void SourceCoverageViewHTML::renderBranchView(raw_ostream &OS, BranchView &BRV, void SourceCoverageViewHTML::renderMCDCView(raw_ostream &OS, MCDCView &MRV, unsigned ViewDepth) { + const bool ShowNonExecutedVectors = getOptions().ShowMCDCNonExecutedVectors; + for (auto &Record : MRV.Records) { OS << BeginExpansionDiv; OS << BeginPre; @@ -1179,10 +1181,33 @@ void SourceCoverageViewHTML::renderMCDCView(raw_ostream &OS, MCDCView &MRV, 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 << " MC/DC Test Vectors\n\n"; + + const unsigned NumExecuted = Record.getNumTestVectors(); + const unsigned NumNotExecuted = Record.getNumNotExecutedTestVectors(); + + const std::string HeaderStr = Record.getTestVectorHeaderString(); + + OS << " Executed:\n\n "; + if (NumExecuted == 0) { + OS << "None.\n"; + } else { + OS << HeaderStr; + for (unsigned k = 0; k < NumExecuted; k++) + OS << Record.getTestVectorString(k); + } + + if (ShowNonExecutedVectors) { + OS << "\n Not executed:\n\n "; + if (NumNotExecuted == 0) { + OS << "None.\n"; + } else { + OS << HeaderStr; + for (unsigned k = 0; k < NumNotExecuted; k++) + OS << Record.getNotExecutedTestVectorString(k); + } + } + OS << "\n"; for (unsigned i = 0; i < Record.getNumConditions(); i++) OS << Record.getConditionCoverageString(i); diff --git a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp index df8eb1d87187..fb47b5541e7a 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp @@ -337,6 +337,8 @@ void SourceCoverageViewText::renderBranchView(raw_ostream &OS, BranchView &BRV, void SourceCoverageViewText::renderMCDCView(raw_ostream &OS, MCDCView &MRV, unsigned ViewDepth) { + const bool ShowNonExecutedVectors = getOptions().ShowMCDCNonExecutedVectors; + for (auto &Record : MRV.Records) { renderLinePrefix(OS, ViewDepth); OS << "---> MC/DC Decision Region ("; @@ -359,16 +361,53 @@ void SourceCoverageViewText::renderMCDCView(raw_ostream &OS, MCDCView &MRV, renderLinePrefix(OS, ViewDepth); OS << "\n"; renderLinePrefix(OS, ViewDepth); - OS << " Executed MC/DC Test Vectors:\n"; + OS << " MC/DC Test Vectors:\n"; renderLinePrefix(OS, ViewDepth); OS << "\n"; + + const unsigned NumExecuted = Record.getNumTestVectors(); + const unsigned NumNotExecuted = Record.getNumNotExecutedTestVectors(); + + const std::string HeaderStr = Record.getTestVectorHeaderString(); + renderLinePrefix(OS, ViewDepth); - OS << " "; - OS << Record.getTestVectorHeaderString(); - for (unsigned i = 0; i < Record.getNumTestVectors(); i++) { + OS << " Executed:\n"; + renderLinePrefix(OS, ViewDepth); + OS << "\n"; + if (NumExecuted == 0) { renderLinePrefix(OS, ViewDepth); - OS << Record.getTestVectorString(i); + OS << " None.\n"; + } else { + renderLinePrefix(OS, ViewDepth); + OS << " "; + OS << HeaderStr; + for (unsigned k = 0; k < NumExecuted; k++) { + renderLinePrefix(OS, ViewDepth); + OS << Record.getTestVectorString(k); + } } + + if (ShowNonExecutedVectors) { + renderLinePrefix(OS, ViewDepth); + OS << "\n"; + renderLinePrefix(OS, ViewDepth); + OS << " Not executed:\n"; + renderLinePrefix(OS, ViewDepth); + OS << "\n"; + if (NumNotExecuted == 0) { + renderLinePrefix(OS, ViewDepth); + OS << " None.\n"; + } else { + renderLinePrefix(OS, ViewDepth); + OS << " "; + OS << HeaderStr; + for (unsigned k = 0; k < NumNotExecuted; k++) { + renderLinePrefix(OS, ViewDepth); + OS << Record.getNotExecutedTestVectorString(k); + } + } + } + renderLinePrefix(OS, ViewDepth); OS << "\n"; for (unsigned i = 0; i < Record.getNumConditions(); i++) {