Skip to content

Commit

Permalink
[coverage] add option to exclude special conditions from MC/DC report
Browse files Browse the repository at this point in the history
  • Loading branch information
Lambdaris committed Jul 13, 2024
1 parent 217f1eb commit e7a5e19
Show file tree
Hide file tree
Showing 14 changed files with 180 additions and 21 deletions.
12 changes: 12 additions & 0 deletions llvm/docs/CommandGuide/llvm-cov.rst
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,12 @@ OPTIONS
Show modified condition/decision coverage (MC/DC) for each applicable boolean
expression.

.. option:: -mcdc-exclude

Set which special states of conditions should be excluded from coverage (MC/DC). Possible
values are: "none", "uncoverable", "constant", "unreachable", separated by comma. Default
to "constant,unreachable".

.. option:: -show-line-counts

Show the execution counts for each line. Defaults to true, unless another
Expand Down Expand Up @@ -435,6 +441,12 @@ OPTIONS

Show MC/DC statistics. Defaults to false.

.. option:: -mcdc-exclude-uncoverable

MC/DC does not count uncoverable conditions. Default to false.
Uncoverable conditions are conditions that may be evaluated but can not affect
the outcome of decisions due to constants.

.. option:: -show-functions

Show coverage summaries for each function. Defaults to false.
Expand Down
15 changes: 9 additions & 6 deletions llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -385,10 +385,10 @@ struct MCDCRecord {
enum CondState { MCDC_DontCare = -1, MCDC_False = 0, MCDC_True = 1 };

enum CondResult {
MCDC_Normal,
MCDC_Constant,
MCDC_Uncoverable,
MCDC_Unreachable
MCDC_Normal = 0x1,
MCDC_Constant = 0x2,
MCDC_Uncoverable = 0x4,
MCDC_Unreachable = 0x8
};

/// Emulate SmallVector<CondState> with a pair of BitVector.
Expand Down Expand Up @@ -527,11 +527,14 @@ struct MCDCRecord {

/// Return if the decision is coverable and percent of covered conditions.
/// Only coverable conditions are counted as denominator.
std::pair<bool, float> getPercentCovered() const {
std::pair<bool, float> getPercentCovered(int32_t CountedStates) const {
unsigned Excluded = 0;
unsigned Covered = 0;
auto IsExcluded = [&](unsigned Condition) {
return (getCondResult(Condition) & CountedStates) == 0;
};
for (unsigned C = 0; C < getNumConditions(); C++) {
if (isCondFolded(C))
if (IsExcluded(C))
Excluded++;
else if (isConditionIndependencePairCovered(C))
Covered++;
Expand Down
25 changes: 25 additions & 0 deletions llvm/test/tools/llvm-cov/Inputs/mcdc-count.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <stdio.h>








void test(bool a,bool b, bool c, bool d) {

if (a && (b || true || c) && ( true || d) && true)
printf("test 1 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);
return 0;
}
Binary file added llvm/test/tools/llvm-cov/Inputs/mcdc-count.o
Binary file not shown.
34 changes: 34 additions & 0 deletions llvm/test/tools/llvm-cov/Inputs/mcdc-count.proftext
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
_Z4testbbbb
# Func Hash:
2877299778974865
# Num Counters:
12
# Counter Values:
5
4
4
4
4
4
0
0
3
0
0
0
# Num Bitmap Bytes:
$3
# Bitmap Byte Values:
0xc0
0x80
0x0


main
# Func Hash:
24
# Num Counters:
1
# Counter Values:
1

50 changes: 50 additions & 0 deletions llvm/test/tools/llvm-cov/mcdc-count.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Test visualization of MC/DC constructs for constant-folded condition masking with different counted states.

// RUN: llvm-profdata merge %S/Inputs/mcdc-count.proftext -o %t.profdata
// RUN: llvm-cov show --show-mcdc %S/Inputs/mcdc-count.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %s -check-prefix=DEFAULTCASE
// RUN: llvm-cov report --show-mcdc-summary %S/Inputs/mcdc-count.o -instr-profile %t.profdata -show-functions -path-equivalence=.,%S/Inputs %S/Inputs/mcdc-count.cpp | FileCheck %s -check-prefix=REPORTDEFAULT

// DEFAULTCASE: | MC/DC Coverage for Decision: 50.00%

// REPORTDEFAULT: TOTAL {{.*}} 2 1 50.00%

// RUN: llvm-cov show --show-mcdc --mcdc-exclude=uncoverable %S/Inputs/mcdc-count.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %s -check-prefix=EXCLUDEUNCOVERABECASE
// RUN: llvm-cov report --show-mcdc-summary --mcdc-exclude=uncoverable %S/Inputs/mcdc-count.o -instr-profile %t.profdata -show-functions -path-equivalence=.,%S/Inputs %S/Inputs/mcdc-count.cpp | FileCheck %s -check-prefix=REPORTEXCLUDEUNCOVERABLE

// EXCLUDEUNCOVERABECASE: | MC/DC Coverage for Decision: 16.67%

// REPORTEXCLUDEUNCOVERABLE: TOTAL {{.*}} 6 5 16.67%

// RUN: llvm-cov show --show-mcdc --mcdc-exclude=constant %S/Inputs/mcdc-count.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %s -check-prefix=EXCLUDECONSTANTCASE
// RUN: llvm-cov report --show-mcdc-summary --mcdc-exclude=constant %S/Inputs/mcdc-count.o -instr-profile %t.profdata -show-functions -path-equivalence=.,%S/Inputs %S/Inputs/mcdc-count.cpp | FileCheck %s -check-prefix=REPORTEXCLUDECONSTANT

// EXCLUDECONSTANTCASE: | MC/DC Coverage for Decision: 25.00%

// REPORTEXCLUDECONSTANT: TOTAL {{.*}} 4 3 25.00%

// RUN: llvm-cov show --show-mcdc --mcdc-exclude=unreachable %S/Inputs/mcdc-count.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %s -check-prefix=EXCLUDEUNREACHABLECASE
// RUN: llvm-cov report --show-mcdc-summary --mcdc-exclude=unreachable %S/Inputs/mcdc-count.o -instr-profile %t.profdata -show-functions -path-equivalence=.,%S/Inputs %S/Inputs/mcdc-count.cpp | FileCheck %s -check-prefix=REPORTEXCLUDEUNREACHABLE

// EXCLUDEUNREACHABLECASE: | MC/DC Coverage for Decision: 20.00%

// REPORTEXCLUDEUNREACHABLE: TOTAL {{.*}} 5 4 20.00%

Instructions for regenerating the test:

cd %S/Inputs # Or copy files into the working directory

clang++ -c -Os \
-fcoverage-compilation-dir=. -mllvm -enable-name-compression=false \
-fcoverage-mcdc -fprofile-instr-generate -fcoverage-mapping \
mcdc-count.cpp

# Instructions for regenerating proftext

for x in mcdc-count; do (
clang++ -fprofile-instr-generate $x.o -o $x
find -name '*.profraw' | xargs rm -f
export LLVM_PROFILE_FILE=$x-%p.profraw
./$x 0 1
llvm-profdata merge --sparse -o $x.profdata $(find -name '*.profraw')
llvm-profdata merge --text -o $x.proftext $x.profdata
); done
25 changes: 25 additions & 0 deletions llvm/tools/llvm-cov/CodeCoverage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,13 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
cl::desc("Show MCDC statistics in summary table"),
cl::init(false));

cl::list<std::string> MCDCExcludeStates(
"mcdc-exclude", cl::Optional,
cl::desc(
"Set which abnormal kinds of conditions are excluded from MC/DC"),
cl::CommaSeparated,
cl::list_init<std::string>({"constant", "unreachable"}));

cl::opt<bool> InstantiationSummary(
"show-instantiation-summary", cl::Optional,
cl::desc("Show instantiation statistics in summary table"));
Expand Down Expand Up @@ -944,6 +951,24 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
::exit(0);
}

ViewOpts.MCDCCountedStates =
MCDCRecord::MCDC_Normal | MCDCRecord::MCDC_Uncoverable |
MCDCRecord::MCDC_Constant | MCDCRecord::MCDC_Unreachable;
for (const auto &State : MCDCExcludeStates) {
if (State == "uncoverable") {
ViewOpts.MCDCCountedStates &= ~MCDCRecord::MCDC_Uncoverable;
} else if (State == "constant") {
ViewOpts.MCDCCountedStates &= ~MCDCRecord::MCDC_Constant;
} else if (State == "unreachable") {
ViewOpts.MCDCCountedStates &= ~MCDCRecord::MCDC_Unreachable;
} else if (State != "none") {
error("invalid argument '" + State +
"', must be in one of 'uncoverable, constant, unreachable'",
"--mcdc-exclude");
return 1;
}
}

ViewOpts.ShowMCDCSummary = MCDCSummary;
ViewOpts.ShowBranchSummary = BranchSummary;
ViewOpts.ShowRegionSummary = RegionSummary;
Expand Down
6 changes: 4 additions & 2 deletions llvm/tools/llvm-cov/CoverageReport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,8 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
OS << "\n";
FunctionCoverageSummary Totals("TOTAL");
for (const auto &F : Functions) {
auto Function = FunctionCoverageSummary::get(Coverage, F);
auto Function =
FunctionCoverageSummary::get(Coverage, F, Options.MCDCCountedStates);
++Totals.ExecutionCount;
Totals.RegionCoverage += Function.RegionCoverage;
Totals.LineCoverage += Function.LineCoverage;
Expand All @@ -453,7 +454,8 @@ void CoverageReport::prepareSingleFileReport(const StringRef Filename,
for (const coverage::FunctionRecord *F : Group.getInstantiations()) {
if (!Filters->matches(*Coverage, *F))
continue;
auto InstantiationSummary = FunctionCoverageSummary::get(*Coverage, *F);
auto InstantiationSummary = FunctionCoverageSummary::get(
*Coverage, *F, Options.MCDCCountedStates);
FileReport->addInstantiation(InstantiationSummary);
InstantiationSummaries.push_back(InstantiationSummary);
}
Expand Down
13 changes: 8 additions & 5 deletions llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//

#include "CoverageSummaryInfo.h"
#include "llvm/ProfileData/Coverage/CoverageMapping.h"

using namespace llvm;
using namespace coverage;
Expand Down Expand Up @@ -44,13 +45,13 @@ static void sumBranchExpansions(size_t &NumBranches, size_t &CoveredBranches,
}
}

static std::pair<size_t, size_t>
sumMCDCPairs(const ArrayRef<MCDCRecord> &Records) {
static std::tuple<size_t, size_t>
sumMCDCPairs(const ArrayRef<MCDCRecord> &Records, const int32_t CountFlags) {
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))
if (Record.getCondResult(C) & CountFlags)
++NumPairs;
if (Record.isConditionIndependencePairCovered(C))
++CoveredPairs;
Expand All @@ -61,7 +62,8 @@ sumMCDCPairs(const ArrayRef<MCDCRecord> &Records) {

FunctionCoverageSummary
FunctionCoverageSummary::get(const CoverageMapping &CM,
const coverage::FunctionRecord &Function) {
const coverage::FunctionRecord &Function,
const int32_t MCDCCountedFlags) {
// Compute the region coverage.
size_t NumCodeRegions = 0, CoveredRegions = 0;
for (auto &CR : Function.CountedRegions) {
Expand Down Expand Up @@ -89,7 +91,8 @@ FunctionCoverageSummary::get(const CoverageMapping &CM,
sumBranchExpansions(NumBranches, CoveredBranches, CM, CD.getExpansions());

size_t NumPairs = 0, CoveredPairs = 0;
std::tie(NumPairs, CoveredPairs) = sumMCDCPairs(CD.getMCDCRecords());
std::tie(NumPairs, CoveredPairs) =
sumMCDCPairs(CD.getMCDCRecords(), MCDCCountedFlags);

return FunctionCoverageSummary(
Function.Name, Function.ExecutionCount,
Expand Down
4 changes: 3 additions & 1 deletion llvm/tools/llvm-cov/CoverageSummaryInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "llvm/ProfileData/Coverage/CoverageMapping.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdint>

namespace llvm {

Expand Down Expand Up @@ -247,7 +248,8 @@ struct FunctionCoverageSummary {
/// Compute the code coverage summary for the given function coverage
/// mapping record.
static FunctionCoverageSummary get(const coverage::CoverageMapping &CM,
const coverage::FunctionRecord &Function);
const coverage::FunctionRecord &Function,
int32_t MCDCCountedFlags = 0);

/// Compute the code coverage summary for an instantiation group \p Group,
/// given a list of summaries for each instantiation in \p Summaries.
Expand Down
1 change: 1 addition & 0 deletions llvm/tools/llvm-cov/CoverageViewOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ struct CoverageViewOptions {
bool SkipExpansions;
bool SkipFunctions;
bool SkipBranches;
int32_t MCDCCountedStates;
OutputFormat Format;
BranchOutputType ShowBranches;
std::string ShowOutputDirectory;
Expand Down
10 changes: 5 additions & 5 deletions llvm/tools/llvm-cov/SourceCoverageView.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,6 @@ class SourceCoverageView {
/// A memory buffer backing the source on display.
const MemoryBuffer &File;

/// Various options to guide the coverage renderer.
const CoverageViewOptions &Options;

/// Complete coverage information about the source on display.
CoverageData CoverageInfo;

Expand Down Expand Up @@ -193,6 +190,9 @@ class SourceCoverageView {

using CoverageSegmentArray = ArrayRef<const CoverageSegment *>;

/// Various options to guide the coverage renderer.
const CoverageViewOptions &Options;

/// @name Rendering Interface
/// @{

Expand Down Expand Up @@ -275,8 +275,8 @@ class SourceCoverageView {
SourceCoverageView(StringRef SourceName, const MemoryBuffer &File,
const CoverageViewOptions &Options,
CoverageData &&CoverageInfo)
: SourceName(SourceName), File(File), Options(Options),
CoverageInfo(std::move(CoverageInfo)) {}
: SourceName(SourceName), File(File),
CoverageInfo(std::move(CoverageInfo)), Options(Options) {}

public:
static std::unique_ptr<SourceCoverageView>
Expand Down
3 changes: 2 additions & 1 deletion llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1200,7 +1200,8 @@ void SourceCoverageViewHTML::renderMCDCView(raw_ostream &OS, MCDCView &MRV,
for (unsigned i = 0; i < Record.getNumConditions(); i++)
OS << Record.getConditionCoverageString(i);
OS << " MC/DC Coverage for Expression: ";
const auto [Coverable, Percent] = Record.getPercentCovered();
const auto [Coverable, Percent] =
Record.getPercentCovered(Options.MCDCCountedStates);
if (Coverable) {
OS << format("%0.2f", Percent) << "%\n";
} else {
Expand Down
3 changes: 2 additions & 1 deletion llvm/tools/llvm-cov/SourceCoverageViewText.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,8 @@ void SourceCoverageViewText::renderMCDCView(raw_ostream &OS, MCDCView &MRV,
}
renderLinePrefix(OS, ViewDepth);
OS << " MC/DC Coverage for Decision: ";
const auto [Coverable, Percent] = Record.getPercentCovered();
const auto [Coverable, Percent] =
Record.getPercentCovered(Options.MCDCCountedStates);
if (Coverable) {
colored_ostream(OS, raw_ostream::RED,
getOptions().Colors && Percent < 100.0,
Expand Down

0 comments on commit e7a5e19

Please sign in to comment.