344 changes: 343 additions & 1 deletion llvm/lib/ProfileData/Coverage/CoverageMapping.cpp

Large diffs are not rendered by default.

33 changes: 30 additions & 3 deletions llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,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.
Expand Down Expand Up @@ -295,6 +296,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");
Expand Down Expand Up @@ -348,9 +370,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,
Expand Down
17 changes: 17 additions & 0 deletions llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 8 additions & 3 deletions llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
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
CHECK-SAME: "summary":{"branches":{"count":0,"covered":0,"notcovered":0,"percent":0},
CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100},
CHECK-SAME: "instantiations":{"count":1,"covered":1,"percent":100},
CHECK-SAME: "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: }
Expand All @@ -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"
Expand Down
117 changes: 117 additions & 0 deletions llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
Binary file not shown.
398 changes: 398 additions & 0 deletions llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.proftext
Original file line number Diff line number Diff line change
@@ -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 changes: 27 additions & 0 deletions llvm/test/tools/llvm-cov/Inputs/mcdc-const.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
Binary file added llvm/test/tools/llvm-cov/Inputs/mcdc-const.o
Binary file not shown.
30 changes: 30 additions & 0 deletions llvm/test/tools/llvm-cov/Inputs/mcdc-const.proftext
Original file line number Diff line number Diff line change
@@ -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 changes: 42 additions & 0 deletions llvm/test/tools/llvm-cov/Inputs/mcdc-general-none.proftext
Original file line number Diff line number Diff line change
@@ -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 changes: 36 additions & 0 deletions llvm/test/tools/llvm-cov/Inputs/mcdc-general.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
Binary file added llvm/test/tools/llvm-cov/Inputs/mcdc-general.o
Binary file not shown.
42 changes: 42 additions & 0 deletions llvm/test/tools/llvm-cov/Inputs/mcdc-general.proftext
Original file line number Diff line number Diff line change
@@ -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 changes: 218 additions & 0 deletions llvm/test/tools/llvm-cov/mcdc-const.test
Original file line number Diff line number Diff line change
@@ -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: | C2-Pair: not covered
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { F, C = F }
// CHECKFULLCASE: | 2 { T, C = F }
// CHECKFULLCASE: | C1-Pair: not covered
// CHECKFULLCASE: | C2-Pair: constant folded
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { C, F = F }
// CHECKFULLCASE: | 2 { C, T = T }
// CHECKFULLCASE: | C1-Pair: constant folded
// CHECKFULLCASE: | C2-Pair: covered: (1,2)
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
// CHECKFULLCASE: | 1 { F, C = F }
// CHECKFULLCASE: | 2 { T, C = T }
// CHECKFULLCASE: | C1-Pair: covered: (1,2)
// CHECKFULLCASE: | C2-Pair: constant folded
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
// CHECKFULLCASE: | 1 { C, - = T }
// CHECKFULLCASE: | C1-Pair: constant folded
// CHECKFULLCASE: | C2-Pair: not covered
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { T, C = T }
// CHECKFULLCASE: | 2 { F, C = T }
// CHECKFULLCASE: | C1-Pair: not covered
// CHECKFULLCASE: | C2-Pair: constant folded
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { C, F = F }
// CHECKFULLCASE: | 2 { C, T = T }
// CHECKFULLCASE: | C1-Pair: constant folded
// CHECKFULLCASE: | C2-Pair: covered: (1,2)
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
// CHECKFULLCASE: | 1 { F, C = F }
// CHECKFULLCASE: | 2 { T, C = T }
// CHECKFULLCASE: | C1-Pair: covered: (1,2)
// CHECKFULLCASE: | C2-Pair: constant folded
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
// CHECKFULLCASE: | 1 { C, -, - = F }
// CHECKFULLCASE: | C1-Pair: constant folded
// CHECKFULLCASE: | C2-Pair: not covered
// CHECKFULLCASE: | C3-Pair: not covered
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { F, C, - = F }
// CHECKFULLCASE: | 2 { T, C, - = F }
// CHECKFULLCASE: | C1-Pair: not covered
// CHECKFULLCASE: | C2-Pair: constant folded
// CHECKFULLCASE: | C3-Pair: not covered
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { C, F, - = F }
// CHECKFULLCASE: | 2 { C, T, F = F }
// CHECKFULLCASE: | 3 { C, T, T = T }
// CHECKFULLCASE: | C1-Pair: constant folded
// CHECKFULLCASE: | C2-Pair: covered: (1,3)
// CHECKFULLCASE: | C3-Pair: covered: (2,3)
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
// CHECKFULLCASE: | 1 { F, C, - = F }
// CHECKFULLCASE: | 2 { T, C, F = F }
// CHECKFULLCASE: | 3 { T, C, T = T }
// CHECKFULLCASE: | C1-Pair: covered: (1,3)
// CHECKFULLCASE: | C2-Pair: constant folded
// CHECKFULLCASE: | C3-Pair: covered: (2,3)
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
// CHECKFULLCASE: | 1 { C, -, - = T }
// CHECKFULLCASE: | C1-Pair: constant folded
// CHECKFULLCASE: | C2-Pair: not covered
// CHECKFULLCASE: | C3-Pair: not covered
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { T, C, - = T }
// CHECKFULLCASE: | 2 { F, C, - = T }
// CHECKFULLCASE: | C1-Pair: not covered
// CHECKFULLCASE: | C2-Pair: constant folded
// CHECKFULLCASE: | C3-Pair: not covered
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { C, F, T = T }
// CHECKFULLCASE: | 2 { C, T, - = T }
// CHECKFULLCASE: | C1-Pair: constant folded
// CHECKFULLCASE: | C2-Pair: not covered
// CHECKFULLCASE: | C3-Pair: not covered
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { T, C, - = T }
// CHECKFULLCASE: | 2 { F, C, T = T }
// CHECKFULLCASE: | C1-Pair: not covered
// CHECKFULLCASE: | C2-Pair: constant folded
// CHECKFULLCASE: | C3-Pair: not covered
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { F, -, C = F }
// CHECKFULLCASE: | 2 { T, F, C = F }
// CHECKFULLCASE: | 3 { T, T, C = F }
// CHECKFULLCASE: | C1-Pair: not covered
// CHECKFULLCASE: | C2-Pair: not covered
// CHECKFULLCASE: | C3-Pair: constant folded
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { F, C, - = F }
// CHECKFULLCASE: | 2 { T, C, - = F }
// CHECKFULLCASE: | C1-Pair: not covered
// CHECKFULLCASE: | C2-Pair: constant folded
// CHECKFULLCASE: | C3-Pair: not covered
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { F, -, C = F }
// CHECKFULLCASE: | 2 { T, F, C = F }
// CHECKFULLCASE: | 3 { T, T, C = T }
// CHECKFULLCASE: | C1-Pair: covered: (1,3)
// CHECKFULLCASE: | C2-Pair: covered: (2,3)
// CHECKFULLCASE: | C3-Pair: constant folded
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
// CHECKFULLCASE: | 1 { F, C, - = F }
// CHECKFULLCASE: | 2 { T, C, F = F }
// CHECKFULLCASE: | 3 { T, C, T = T }
// CHECKFULLCASE: | C1-Pair: covered: (1,3)
// CHECKFULLCASE: | C2-Pair: constant folded
// CHECKFULLCASE: | C3-Pair: covered: (2,3)
// CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00%
// CHECKFULLCASE: | 1 { T, -, C = T }
// CHECKFULLCASE: | 2 { F, T, C = T }
// CHECKFULLCASE: | C1-Pair: not covered
// CHECKFULLCASE: | C2-Pair: not covered
// CHECKFULLCASE: | C3-Pair: constant folded
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { T, C, - = T }
// CHECKFULLCASE: | 2 { F, C, - = T }
// CHECKFULLCASE: | C1-Pair: not covered
// CHECKFULLCASE: | C2-Pair: constant folded
// CHECKFULLCASE: | C3-Pair: not covered
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { T, -, C = T }
// CHECKFULLCASE: | 2 { F, T, C = T }
// CHECKFULLCASE: | C1-Pair: not covered
// CHECKFULLCASE: | C2-Pair: not covered
// CHECKFULLCASE: | C3-Pair: constant folded
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%
// CHECKFULLCASE: | 1 { T, C, - = T }
// CHECKFULLCASE: | 2 { F, C, T = T }
// CHECKFULLCASE: | C1-Pair: not covered
// CHECKFULLCASE: | C2-Pair: constant folded
// CHECKFULLCASE: | C3-Pair: not covered
// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00%

// REPORT: _Z5case0b {{.*}} 1 1 0.00%
// REPORT: _Z5case1b {{.*}} 1 1 0.00%
// REPORT: _Z5case2b {{.*}} 1 0 100.00%
// REPORT: _Z5case3b {{.*}} 1 0 100.00%
// REPORT: _Z5case4b {{.*}} 1 1 0.00%
// REPORT: _Z5case5b {{.*}} 1 1 0.00%
// REPORT: _Z5case6b {{.*}} 1 0 100.00%
// REPORT: _Z5case7b {{.*}} 1 0 100.00%
// REPORT: _Z5case8bb {{.*}} 2 2 0.00%
// REPORT: _Z5case9bb {{.*}} 2 2 0.00%
// REPORT: _Z5caseabb {{.*}} 2 0 100.00%
// REPORT: _Z5casebbb {{.*}} 2 0 100.00%
// REPORT: _Z5casecbb {{.*}} 2 2 0.00%
// REPORT: _Z5casedbb {{.*}} 2 2 0.00%
// REPORT: _Z5caseebb {{.*}} 2 2 0.00%
// REPORT: _Z5casefbb {{.*}} 2 2 0.00%
// REPORT: _Z5casegbb {{.*}} 2 2 0.00%
// REPORT: _Z5casehbb {{.*}} 2 2 0.00%
// REPORT: _Z5caseibb {{.*}} 2 0 100.00%
// REPORT: _Z5casejbb {{.*}} 2 0 100.00%
// REPORT: _Z5casekbb {{.*}} 2 2 0.00%
// REPORT: _Z5caselbb {{.*}} 2 2 0.00%
// REPORT: _Z5casembb {{.*}} 2 2 0.00%
// REPORT: _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-mapping /tmp/mcdc-const.cpp -o /tmp/mcdc-const.o

clang -fcoverage-mcdc -fprofile-instr-generate \
-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 changes: 18 additions & 0 deletions llvm/test/tools/llvm-cov/mcdc-export-json.test
Original file line number Diff line number Diff line change
@@ -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-mapping /tmp/mcdc-general.cpp -o /tmp/mcdc-const.o

mv /tmp/mcdc-general.o %S/Inputs
82 changes: 82 additions & 0 deletions llvm/test/tools/llvm-cov/mcdc-general-none.test
Original file line number Diff line number Diff line change
@@ -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-mapping /tmp/mcdc-general.cpp -o /tmp/mcdc-const.o

mv /tmp/mcdc-general.o %S/Inputs
148 changes: 148 additions & 0 deletions llvm/test/tools/llvm-cov/mcdc-general.test
Original file line number Diff line number Diff line change
@@ -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-mapping /tmp/mcdc-general.cpp -o /tmp/mcdc-general.o

mv /tmp/mcdc-general.o %S/Inputs
55 changes: 55 additions & 0 deletions llvm/tools/llvm-cov/CodeCoverage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ class CodeCoverageTool {
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,
Expand Down Expand Up @@ -352,6 +357,36 @@ 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()) {
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) {
Expand All @@ -364,12 +399,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;
}
Expand All @@ -386,11 +424,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;

Expand All @@ -408,11 +449,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;
Expand Down Expand Up @@ -752,6 +796,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"));
Expand Down Expand Up @@ -923,6 +971,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;
Expand Down Expand Up @@ -968,6 +1017,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 "
Expand Down Expand Up @@ -1063,6 +1117,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;
Expand Down
44 changes: 43 additions & 1 deletion llvm/tools/llvm-cov/CoverageExporterJson.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
//
//===----------------------------------------------------------------------===//

Expand Down Expand Up @@ -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)
Expand All @@ -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) {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
47 changes: 44 additions & 3 deletions llvm/tools/llvm-cov/CoverageReport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,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,
Expand Down Expand Up @@ -292,6 +292,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";
}

Expand Down Expand Up @@ -339,6 +355,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";
}

Expand Down Expand Up @@ -371,6 +400,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";
Expand All @@ -381,6 +415,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) {
Expand Down Expand Up @@ -503,6 +538,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";
Expand Down
19 changes: 18 additions & 1 deletion llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ static void sumBranchExpansions(size_t &NumBranches, size_t &CoveredBranches,
}
}

static void sumMCDCPairs(size_t &NumPairs, size_t &CoveredPairs,
const ArrayRef<MCDCRecord> &Records) {
for (const auto &Record : Records)
for (unsigned C = 0; C < Record.getNumConditions(); C++) {
if (!Record.isCondFolded(C))
++NumPairs;
if (Record.isConditionIndependencePairCovered(C))
++CoveredPairs;
}
}

FunctionCoverageSummary
FunctionCoverageSummary::get(const CoverageMapping &CM,
const coverage::FunctionRecord &Function) {
Expand Down Expand Up @@ -73,11 +84,15 @@ FunctionCoverageSummary::get(const CoverageMapping &CM,
sumBranches(NumBranches, CoveredBranches, CD.getBranches());
sumBranchExpansions(NumBranches, CoveredBranches, CM, CD.getExpansions());

size_t NumPairs = 0, CoveredPairs = 0;
sumMCDCPairs(NumPairs, CoveredPairs, CD.getMCDCRecords());

return FunctionCoverageSummary(
Function.Name, Function.ExecutionCount,
RegionCoverageInfo(CoveredRegions, NumCodeRegions),
LineCoverageInfo(CoveredLines, NumLines),
BranchCoverageInfo(CoveredBranches, NumBranches));
BranchCoverageInfo(CoveredBranches, NumBranches),
MCDCCoverageInfo(CoveredPairs, NumPairs));
}

FunctionCoverageSummary
Expand All @@ -97,10 +112,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;
}
50 changes: 48 additions & 2 deletions llvm/tools/llvm-cov/CoverageSummaryInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,47 @@ class BranchCoverageInfo {
}
};

/// Provides information about branches 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.
Expand Down Expand Up @@ -189,17 +230,19 @@ struct FunctionCoverageSummary {
RegionCoverageInfo RegionCoverage;
LineCoverageInfo LineCoverage;
BranchCoverageInfo BranchCoverage;
MCDCCoverageInfo MCDCCoverage;

FunctionCoverageSummary(const std::string &Name)
: Name(Name), ExecutionCount(0) {}

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.
Expand All @@ -219,6 +262,7 @@ struct FileCoverageSummary {
RegionCoverageInfo RegionCoverage;
LineCoverageInfo LineCoverage;
BranchCoverageInfo BranchCoverage;
MCDCCoverageInfo MCDCCoverage;
FunctionCoverageInfo FunctionCoverage;
FunctionCoverageInfo InstantiationCoverage;

Expand All @@ -230,6 +274,7 @@ struct FileCoverageSummary {
LineCoverage += RHS.LineCoverage;
FunctionCoverage += RHS.FunctionCoverage;
BranchCoverage += RHS.BranchCoverage;
MCDCCoverage += RHS.MCDCCoverage;
InstantiationCoverage += RHS.InstantiationCoverage;
return *this;
}
Expand All @@ -238,6 +283,7 @@ struct FileCoverageSummary {
RegionCoverage += Function.RegionCoverage;
LineCoverage += Function.LineCoverage;
BranchCoverage += Function.BranchCoverage;
MCDCCoverage += Function.MCDCCoverage;
FunctionCoverage.addFunction(/*Covered=*/Function.ExecutionCount > 0);
}

Expand Down
2 changes: 2 additions & 0 deletions llvm/tools/llvm-cov/CoverageViewOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions llvm/tools/llvm-cov/SourceCoverageView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
28 changes: 28 additions & 0 deletions llvm/tools/llvm-cov/SourceCoverageView.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -233,6 +253,10 @@ class SourceCoverageView {
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;
Expand Down Expand Up @@ -283,6 +307,10 @@ class SourceCoverageView {
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,
Expand Down
52 changes: 52 additions & 0 deletions llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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(), ""));
}

Expand Down Expand Up @@ -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 = utostr(uint64_t(DecisionRegion.LineStart));
std::string ColNoStr = utostr(uint64_t(DecisionRegion.ColumnStart));
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) {
Expand Down
3 changes: 3 additions & 0 deletions llvm/tools/llvm-cov/SourceCoverageViewHTML.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
51 changes: 51 additions & 0 deletions llvm/tools/llvm-cov/SourceCoverageViewText.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions llvm/tools/llvm-cov/SourceCoverageViewText.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
46 changes: 46 additions & 0 deletions llvm/unittests/ProfileData/CoverageMappingTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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);

Expand Down