311 changes: 311 additions & 0 deletions llvm/test/tools/llvm-cov/branch-c-general.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
// Test visualization of general branch constructs in C.

// RUN: llvm-profdata merge %S/Inputs/branch-c-general.proftext -o %t.profdata
// RUN: llvm-cov show --show-branches=count %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=REPORT

void simple_loops() {
int i;
for (i = 0; i < 100; ++i) { // CHECK: Branch ([[@LINE]]:15): [True: 100, False: 1]
}
while (i > 0) // CHECK: Branch ([[@LINE]]:10): [True: 100, False: 1]
i--;
do {} while (i++ < 75); // CHECK: Branch ([[@LINE]]:16): [True: 75, False: 1]

}

void conditionals() {
for (int i = 0; i < 100; ++i) {// CHECK: Branch ([[@LINE]]:19): [True: 100, False: 1]
if (i % 2) { // CHECK: Branch ([[@LINE]]:9): [True: 50, False: 50]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 50, False: 0]
} else if (i % 3) { // CHECK: Branch ([[@LINE]]:16): [True: 33, False: 17]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 33, False: 0]
} else {
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 16, False: 1]
}
// CHECK: Branch ([[@LINE+1]]:9): [Folded - Ignored]
if (1 && i) {} // CHECK: Branch ([[@LINE]]:14): [True: 99, False: 1]
if (0 || i) {} // CHECK: Branch ([[@LINE]]:9): [Folded - Ignored]
} // CHECK: Branch ([[@LINE-1]]:14): [True: 99, False: 1]

}

void early_exits() {
int i = 0;

if (i) {} // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 1]

while (i < 100) { // CHECK: Branch ([[@LINE]]:10): [True: 51, False: 0]
i++;
if (i > 50) // CHECK: Branch ([[@LINE]]:9): [True: 1, False: 50]
break;
if (i % 2) // CHECK: Branch ([[@LINE]]:9): [True: 25, False: 25]
continue;
}

if (i) {} // CHECK: Branch ([[@LINE]]:7): [True: 1, False: 0]

do {
if (i > 75) // CHECK: Branch ([[@LINE]]:9): [True: 1, False: 25]
return;
else
i++;
} while (i < 100); // CHECK: Branch ([[@LINE]]:12): [True: 25, False: 0]

if (i) {} // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 0]

}

void jumps() {
int i;

for (i = 0; i < 2; ++i) { // CHECK: Branch ([[@LINE]]:15): [True: 1, False: 0]
goto outofloop;
// Never reached -> no weights
if (i) {} // CHECK: Branch ([[@LINE]]:9): [True: 0, False: 0]
}

outofloop:
if (i) {} // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 1]

goto loop1;

while (i) { // CHECK: Branch ([[@LINE]]:10): [True: 0, False: 1]
loop1:
if (i) {} // CHECK: Branch ([[@LINE]]:9): [True: 0, False: 1]
}

goto loop2;
first:
second:
third:
i++;
if (i < 3) // CHECK: Branch ([[@LINE]]:7): [True: 2, False: 1]
goto loop2;

while (i < 3) { // CHECK: Branch ([[@LINE]]:10): [True: 0, False: 1]
loop2:
switch (i) {
case 0: // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 2]
goto first;
case 1: // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 2]
goto second;
case 2: // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 2]
goto third;
}
}

for (i = 0; i < 10; ++i) { // CHECK: Branch ([[@LINE]]:15): [True: 10, False: 1]
goto withinloop;
// never reached -> no weights
if (i) {} // CHECK: Branch ([[@LINE]]:9): [True: 0, False: 0]
withinloop:
if (i) {} // CHECK: Branch ([[@LINE]]:9): [True: 9, False: 1]
}

}

void switches() {
static int weights[] = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5};

// No cases -> no weights
switch (weights[0]) {
default: // CHECK: Branch ([[@LINE]]:3): [True: 1, False: 0]
break;
}
// CHECK: Branch ([[@LINE+1]]:63): [True: 15, False: 0]
for (int i = 0, len = sizeof(weights) / sizeof(weights[0]); i < len; ++i) {
switch (i[weights]) {
case 1: // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 14]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 0, False: 1]
// fallthrough
case 2: // CHECK: Branch ([[@LINE]]:5): [True: 2, False: 13]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 2, False: 1]
break;
case 3: // CHECK: Branch ([[@LINE]]:5): [True: 3, False: 12]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 3, False: 0]
continue;
case 4: // CHECK: Branch ([[@LINE]]:5): [True: 4, False: 11]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 4, False: 0]
switch (i) {
case 6 ... 9: // CHECK: Branch ([[@LINE]]:7): [True: 4, False: 0]
if (i) {} // CHECK: Branch ([[@LINE]]:13): [True: 4, False: 0]
continue;
}

default: // CHECK: Branch ([[@LINE]]:5): [True: 5, False: 10]
if (i == len - 1) // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 4]
return;
}
}

// Never reached -> no weights
if (weights[0]) {} // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 0]

}

void big_switch() {
for (int i = 0; i < 32; ++i) {// CHECK: Branch ([[@LINE]]:19): [True: 32, False: 1]
switch (1 << i) {
case (1 << 0): // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 31]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 0, False: 1]
// fallthrough
case (1 << 1): // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 31]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 1]
break;
case (1 << 2) ... (1 << 12):// CHECK: Branch ([[@LINE]]:5): [True: 11, False: 21]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 11, False: 0]
break;
// The branch for the large case range above appears after the case body.

case (1 << 13): // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 31]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 0]
break;
case (1 << 14) ... (1 << 28):// CHECK: Branch ([[@LINE]]:5): [True: 15, False: 17]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 15, False: 0]
break;
// The branch for the large case range above appears after the case body.
// CHECK: Branch ([[@LINE+1]]:5): [True: 1, False: 31]
case (1 << 29) ... ((1 << 29) + 1):
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 0]
break;
default: // CHECK: Branch ([[@LINE]]:5): [True: 2, False: 30]
if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 2, False: 0]
break;
}
}

}

void boolean_operators() {
int v; // CHECK: Branch ([[@LINE+1]]:19): [True: 100, False: 1]
for (int i = 0; i < 100; ++i) {
v = i % 3 || i; // CHECK: Branch ([[@LINE]]:9): [True: 66, False: 34]
// CHECK: Branch ([[@LINE-1]]:18): [True: 33, False: 1]
v = i % 3 && i; // CHECK: Branch ([[@LINE]]:9): [True: 66, False: 34]
// CHECK: Branch ([[@LINE-1]]:18): [True: 66, False: 0]
v = i % 3 || i % 2 || i; // CHECK: Branch ([[@LINE]]:9): [True: 66, False: 34]
// CHECK: Branch ([[@LINE-1]]:18): [True: 17, False: 17]
v = i % 2 && i % 3 && i; // CHECK: Branch ([[@LINE-2]]:27): [True: 16, False: 1]
} // CHECK: Branch ([[@LINE-1]]:9): [True: 50, False: 50]
// CHECK: Branch ([[@LINE-2]]:18): [True: 33, False: 17]
} // CHECK: Branch ([[@LINE-3]]:27): [True: 33, False: 0]

void boolop_loops() {
int i = 100;

while (i && i > 50) // CHECK: Branch ([[@LINE]]:10): [True: 51, False: 0]
i--; // CHECK: Branch ([[@LINE-1]]:15): [True: 50, False: 1]

while ((i % 2) || (i > 0)) // CHECK: Branch ([[@LINE]]:10): [True: 25, False: 26]
i--; // CHECK: Branch ([[@LINE-1]]:21): [True: 25, False: 1]

for (i = 100; i && i > 50; --i); // CHECK: Branch ([[@LINE]]:17): [True: 51, False: 0]
// CHECK: Branch ([[@LINE-1]]:22): [True: 50, False: 1]
for (; (i % 2) || (i > 0); --i); // CHECK: Branch ([[@LINE]]:10): [True: 25, False: 26]
// CHECK: Branch ([[@LINE-1]]:21): [True: 25, False: 1]
}

void conditional_operator() {
int i = 100;

int j = i < 50 ? i : 1; // CHECK: Branch ([[@LINE]]:11): [True: 0, False: 1]

int k = i ?: 0; // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 0]

}

void do_fallthrough() {
for (int i = 0; i < 10; ++i) {// CHECK: Branch ([[@LINE]]:19): [True: 10, False: 1]
int j = 0;
do {
// The number of exits out of this do-loop via the break statement
// exceeds the counter value for the loop (which does not include the
// fallthrough count). Make sure that does not violate any assertions.
if (i < 8) break; // CHECK: Branch ([[@LINE]]:11): [True: 8, False: 4]
j++;
} while (j < 2); // CHECK: Branch ([[@LINE]]:14): [True: 2, False: 2]
}
}

static void static_func() {
for (int i = 0; i < 10; ++i) {// CHECK: Branch ([[@LINE]]:19): [True: 10, False: 1]
}
}










int main(int argc, const char *argv[]) {
simple_loops();
conditionals();
early_exits();
jumps();
switches();
big_switch();
boolean_operators();
boolop_loops();
conditional_operator();
do_fallthrough();
static_func();
extern void __llvm_profile_write_file();
__llvm_profile_write_file();
return 0;
}

// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover
// REPORT-NEXT: ---
// REPORT-NEXT: simple_loops 8 0 100.00% 9 0 100.00% 6 0 100.00%
// REPORT-NEXT: conditionals 24 0 100.00% 15 0 100.00% 16 2 87.50%
// REPORT-NEXT: early_exits 20 4 80.00% 25 3 88.00% 16 6 62.50%
// REPORT-NEXT: jumps 39 12 69.23% 48 4 91.67% 26 9 65.38%
// REPORT-NEXT: switches 28 5 82.14% 38 5 86.84% 30 9 70.00%
// REPORT-NEXT: big_switch 25 1 96.00% 32 0 100.00% 30 6 80.00%
// REPORT-NEXT: boolean_operators 16 0 100.00% 13 0 100.00% 22 2 90.91%
// REPORT-NEXT: boolop_loops 19 0 100.00% 14 0 100.00% 16 2 87.50%
// REPORT-NEXT: conditional_operator 4 2 50.00% 8 1 87.50% 4 2 50.00%
// REPORT-NEXT: do_fallthrough 9 0 100.00% 12 0 100.00% 6 0 100.00%
// REPORT-NEXT: main 1 0 100.00% 16 0 100.00% 0 0 0.00%
// REPORT-NEXT: c-general.c:static_func 4 0 100.00% 4 0 100.00% 2 0 100.00%
// REPORT-NEXT: ---
// REPORT-NEXT: TOTAL 197 24 87.82% 234 13 94.44% 174 38 78.16%

// Test file-level report.
// RUN: llvm-profdata merge %S/Inputs/branch-c-general.proftext -o %t.profdata
// RUN: llvm-cov report %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=FILEREPORT
// FILEREPORT: TOTAL{{.*}}174 38 78.16%

// Test color True/False output.
// RUN: llvm-cov show --use-color --show-branches=count %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=USECOLOR
// USECOLOR: Branch ({{[0-9]+}}:7): {{.*}}: 0, {{.*}}0]

// Test html output.
// RUN: llvm-cov show --show-branch-summary --show-branches=count %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s -format html -o %t.html.dir
// RUN: FileCheck -check-prefix=HTML -input-file=%t.html.dir/coverage/tmp/branch-c-general.c.html %s
// HTML-COUNT-89: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>
// HTML-NOT: Branch (<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: <a href='coverage{{.*}}branch-c-general.c.html'{{.*}}branch-c-general.c</a>
// HTML-INDEX: <td class='column-entry-green'>
// HTML-INDEX: 100.00% (12/12)
// HTML-INDEX: <td class='column-entry-yellow'>
// HTML-INDEX: 94.44% (221/234)
// HTML-INDEX: <td class='column-entry-yellow'>
// HTML-INDEX: 87.82% (173/197)
// HTML-INDEX: <td class='column-entry-red'>
// HTML-INDEX: 78.16% (136/174)
// HTML-INDEX: <tr class='light-row-bold'>
// HTML-INDEX: Totals
49 changes: 49 additions & 0 deletions llvm/test/tools/llvm-cov/branch-export-json.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@

// RUN: llvm-profdata merge %S/Inputs/branch-showBranchPercentage.proftext -o %t.profdata
// RUN: llvm-cov export --format=text %S/Inputs/branch-showBranchPercentage.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-showBranchPercentage.c | FileCheck %s

// CHECK: "branches":
// CHECK: 14,7,14,15,1,5,0,0,4
// CHECK: 27,8,27,14,1,4,0,0,4
// CHECK: 27,18,27,24,0,1,0,0,4
// CHECK: 27,29,27,36,0,5,0,0,4
// CHECK: 27,40,27,46,2,3,0,0,4
// CHECK: 30,8,30,14,4,1,0,0,4
// CHECK: 30,18,30,24,0,1,0,0,4
// CHECK: 32,8,32,14,4,1,0,0,4
// CHECK: 32,18,32,24,1,3,0,0,4
// CHECK: 34,15,34,20,1,5,0,0,4
// CHECK: 41,5,41,11,1,4,0,0,4
// CHECK: 43,5,43,11,1,4,0,0,4
// CHECK: 45,5,45,11,0,5,0,0,4
// CHECK: 47,5,47,12,3,2,0,0,4
// CHECK: 53,12,53,20,50,5,0,0,4
// CHECK: {"count":30,"covered":26,"notcovered":4,"percent":86.666666666666671}

// Check recursive macro-expansions.
// RUN: llvm-profdata merge %S/Inputs/branch-macros.proftext -o %t.profdata
// RUN: llvm-cov export --format=text %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-macros.c | FileCheck %s -check-prefix=MACROS

// MACROS: "branches":
// MACROS: 27,10,27,11,0,3,0,0,4
// MACROS: 27,15,27,16,0,0,0,0,4
// MACROS: 27,20,27,21,0,0,0,0,4
// MACROS: 27,25,27,26,0,0,0,0,4
// MACROS: 27,30,27,31,0,0,0,0,4

// MACROS: 15,5,23,1,2,1,0,4
// MACROS: 6,15,6,23,0,1,2,0,4
// MACROS: 5,15,5,23,1,2,7,0,4
// MACROS: 6,15,6,23,0,1,8,0,4
// MACROS: 5,15,5,23,1,2,12,0,4
// MACROS: 6,15,6,23,0,1,13,0,4
// MACROS: 5,15,5,23,1,2,16,0,4
// MACROS: 6,15,6,23,0,1,17,0,4
// MACROS: 5,15,5,23,1,2,19,0,4
// MACROS: 6,15,6,23,0,1,20,0,4
// MACROS: 5,15,5,23,1,2,11,0,4
// MACROS: 6,15,6,23,0,1,12,0,4
// MACROS: 5,15,5,23,1,2,8,0,4
// MACROS: 6,15,6,23,0,1,9,0,4
// MACROS: 8,15,8,38,1,2,2,0,4
// MACROS: {"count":40,"covered":24,"notcovered":16,"percent":60}
73 changes: 73 additions & 0 deletions llvm/test/tools/llvm-cov/branch-export-lcov.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

// RUN: llvm-profdata merge %S/Inputs/branch-showBranchPercentage.proftext -o %t.profdata
// RUN: llvm-cov export --format=lcov %S/Inputs/branch-showBranchPercentage.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-showBranchPercentage.c | FileCheck %s

// CHECK-DAG: BRDA:14,0,0,1
// CHECK-DAG: BRDA:14,0,1,5
// CHECK-DAG: BRDA:27,0,0,1
// CHECK-DAG: BRDA:27,0,1,4
// CHECK-DAG: BRDA:27,1,2,0
// CHECK-DAG: BRDA:27,1,3,1
// CHECK-DAG: BRDA:27,2,4,0
// CHECK-DAG: BRDA:27,2,5,5
// CHECK-DAG: BRDA:27,3,6,2
// CHECK-DAG: BRDA:27,3,7,3
// CHECK-DAG: BRDA:30,0,0,4
// CHECK-DAG: BRDA:30,0,1,1
// CHECK-DAG: BRDA:30,1,2,0
// CHECK-DAG: BRDA:30,1,3,1
// CHECK-DAG: BRDA:32,0,0,4
// CHECK-DAG: BRDA:32,0,1,1
// CHECK-DAG: BRDA:32,1,2,1
// CHECK-DAG: BRDA:32,1,3,3
// CHECK-DAG: BRDA:34,0,0,1
// CHECK-DAG: BRDA:34,0,1,5
// CHECK-DAG: BRDA:41,0,0,1
// CHECK-DAG: BRDA:41,0,1,4
// CHECK-DAG: BRDA:43,0,0,1
// CHECK-DAG: BRDA:43,0,1,4
// CHECK-DAG: BRDA:45,0,0,0
// CHECK-DAG: BRDA:45,0,1,5
// CHECK-DAG: BRDA:47,0,0,3
// CHECK-DAG: BRDA:47,0,1,2
// CHECK-DAG: BRDA:53,0,0,50
// CHECK-DAG: BRDA:53,0,1,5
// CHECK-NOT: BRDA
// CHECK: BRF:30
// CHECK: BFH:26

// Check recursive macro-expansions.
// RUN: llvm-profdata merge %S/Inputs/branch-macros.proftext -o %t.profdata
// RUN: llvm-cov export --format=lcov %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-macros.cpp | FileCheck %s -check-prefix=MACROS

// MACROS-COUNT-4: BRDA:17
// MACROS-NOT: BRDA:17

// MACROS-COUNT-4: BRDA:19
// MACROS-NOT: BRDA:19

// MACROS-COUNT-4: BRDA:21
// MACROS-NOT: BRDA:21

// MACROS-COUNT-4: BRDA:23
// MACROS-NOT: BRDA:23

// MACROS-COUNT-4: BRDA:25
// MACROS-NOT: BRDA:25

// MACROS: BRDA:27,0,0,0
// MACROS: BRDA:27,0,1,3
// MACROS: BRDA:27,1,2,-
// MACROS: BRDA:27,1,3,-
// MACROS: BRDA:27,2,4,-
// MACROS: BRDA:27,2,5,-
// MACROS: BRDA:27,3,6,-
// MACROS: BRDA:27,3,7,-
// MACROS: BRDA:27,4,8,-
// MACROS: BRDA:27,4,9,-

// MACROS-COUNT-10: BRDA:37
// MACROS-NOT: BRDA:37
// MACROS-NOT: BRDA
// MACROS: BRF:40
// MACROS: BFH:24
90 changes: 90 additions & 0 deletions llvm/test/tools/llvm-cov/branch-logical-mixed.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// RUN: llvm-profdata merge %S/Inputs/branch-logical-mixed.proftext -o %t.profdata
// RUN: llvm-cov show --show-branches=count %S/Inputs/branch-logical-mixed.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-logical-mixed.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=REPORT

#include <stdio.h>
#include <stdlib.h>

void func(int a, int b) {
bool b0 = a <= b;
bool b1 = a == b;
bool b2 = a >= b;
bool b3 = a < b;
bool b4 = a > b;
bool b5 = a != b;

bool c = b0 && // CHECK: Branch ([[@LINE]]:12): [True: 3, False: 1]
b1 && // CHECK: Branch ([[@LINE]]:12): [True: 2, False: 1]
b2 && // CHECK: Branch ([[@LINE]]:12): [True: 2, False: 0]
b3 && // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 2]
b4 && // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0]
b5; // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0]

bool d = b0 || // CHECK: Branch ([[@LINE]]:12): [True: 3, False: 1]
b1 || // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 1]
b2 || // CHECK: Branch ([[@LINE]]:12): [True: 1, False: 0]
b3 || // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0]
b4 || // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0]
b5; // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0]

bool e = (b0 && // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 1]
b5) || // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2]
(b1 && // CHECK: Branch ([[@LINE]]:13): [True: 2, False: 1]
b4) || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 2]
(b2 && // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 0]
b3) || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 3]
(b3 && // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 3]
b2) || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 0]
(b4 && // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2]
b1) || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 1]
(b5 && // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2]
b0); // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 1]

bool f = (b0 || // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 1]
b5) && // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 0]
(b1 || // CHECK: Branch ([[@LINE]]:13): [True: 2, False: 2]
b4) && // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 1]
(b2 || // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 0]
b3) && // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 0]
(b3 || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 3]
b2) && // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 0]
(b4 || // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2]
b1) && // CHECK: Branch ([[@LINE]]:13): [True: 2, False: 0]
(b5 || // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2]
b0); // CHECK: Branch ([[@LINE]]:13): [True: 2, False: 0]

if (c) // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 4]
printf("case0\n");
else
printf("case1\n");

if (d) // CHECK: Branch ([[@LINE]]:7): [True: 4, False: 0]
printf("case2\n");
else
printf("case3\n");

if (e) // CHECK: Branch ([[@LINE]]:7): [True: 1, False: 3]
printf("case4\n");
else
printf("case5\n");

if (f) // CHECK: Branch ([[@LINE]]:7): [True: 3, False: 1]
printf("case6\n");
else
printf("case7\n");
}

extern "C" { extern void __llvm_profile_write_file(void); }
int main(int argc, char *argv[])
{
func(atoi(argv[1]), atoi(argv[2]));
__llvm_profile_write_file();
return 0;
}

// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover
// REPORT-NEXT: ---
// REPORT-NEXT: _Z4funcii 77 9 88.31% 68 10 85.29% 80 32 60.00%
// REPORT-NEXT: main 1 0 100.00% 5 0 100.00% 0 0 0.00%
// REPORT-NEXT: ---
// REPORT-NEXT: TOTAL 78 9 88.46% 73 10 86.30% 80 32 60.00%
60 changes: 60 additions & 0 deletions llvm/test/tools/llvm-cov/branch-macros.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// RUN: llvm-profdata merge %S/Inputs/branch-macros.proftext -o %t.profdata
// RUN: llvm-cov show --show-expansions --show-branches=count %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=REPORT

#define COND1 (a == b)
#define COND2 (a != b)
#define COND3 (COND1 && COND2)
#define COND4 (COND3 ? COND2 : COND1)
#define MACRO1 COND3
#define MACRO2 MACRO1
#define MACRO3 MACRO2

#include <stdlib.h>


bool func(int a, int b) {
bool c = COND1 && COND2; // CHECK: | | | Branch ([[@LINE-12]]:15): [True: 1, False: 2]
// CHECK: | | | Branch ([[@LINE-12]]:15): [True: 0, False: 1]
bool d = COND3; // CHECK: | | | | | Branch ([[@LINE-14]]:15): [True: 1, False: 2]
// CHECK: | | | | | Branch ([[@LINE-14]]:15): [True: 0, False: 1]
bool e = MACRO1; // CHECK: | | | | | | | Branch ([[@LINE-16]]:15): [True: 1, False: 2]
// CHECK: | | | | | | | Branch ([[@LINE-16]]:15): [True: 0, False: 1]
bool f = MACRO2; // CHECK: | | | | | | | | | Branch ([[@LINE-18]]:15): [True: 1, False: 2]
// CHECK: | | | | | | | | | Branch ([[@LINE-18]]:15): [True: 0, False: 1]
bool g = MACRO3; // CHECK: | | | | | | | | | | | Branch ([[@LINE-20]]:15): [True: 1, False: 2]
// CHECK: | | | | | | | | | | | Branch ([[@LINE-20]]:15): [True: 0, False: 1]
return c && d && e && f && g;
// CHECK: | Branch ([[@LINE-1]]:10): [True: 0, False: 3]
// CHECK: | Branch ([[@LINE-2]]:15): [True: 0, False: 0]
// CHECK: | Branch ([[@LINE-3]]:20): [True: 0, False: 0]
// CHECK: | Branch ([[@LINE-4]]:25): [True: 0, False: 0]
// CHECK: | Branch ([[@LINE-5]]:30): [True: 0, False: 0]
}


bool func2(int a, int b) {
bool h = MACRO3 || COND4; // CHECK: | | | | | | | | | | | Branch ([[@LINE-32]]:15): [True: 1, False: 2]
// CHECK: | | | | | | | | | | | Branch ([[@LINE-32]]:15): [True: 0, False: 1]
// CHECK: | | | | | | | Branch ([[@LINE-34]]:15): [True: 1, False: 2]
// CHECK: | | | | | | | Branch ([[@LINE-34]]:15): [True: 0, False: 1]
// CHECK: | | | Branch ([[@LINE-33]]:15): [True: 1, False: 2]
return h;
}

extern "C" { extern void __llvm_profile_write_file(void); }
int main(int argc, char *argv[])
{
func(atoi(argv[1]), atoi(argv[2]));
func2(atoi(argv[1]), atoi(argv[2]));
__llvm_profile_write_file();
return 0;
}

// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover
// REPORT-NEXT: ---
// REPORT-NEXT: _Z4funcii 28 4 85.71% 18 0 100.00% 30 14 53.33%
// REPORT-NEXT: _Z5func2ii 13 1 92.31% 8 0 100.00% 10 2 80.00%
// REPORT-NEXT: main 1 0 100.00% 6 0 100.00% 0 0 0.00%
// REPORT-NEXT: ---
// REPORT-NEXT: TOTAL 42 5 88.10% 32 0 100.00% 40 16 60.00%
25 changes: 25 additions & 0 deletions llvm/test/tools/llvm-cov/branch-noShowBranch.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

// RUN: llvm-profdata merge %S/Inputs/branch-c-general.proftext -o %t.profdata
// RUN: llvm-cov show %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-c-general.c | FileCheck %s
// RUN: llvm-cov report %S/Inputs/branch-c-general.o32l --show-branch-summary=false -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %S/branch-c-general.c | FileCheck %s -check-prefix=REPORT

// CHECK-NOT: | Branch

// REPORT: Name Regions Miss Cover Lines Miss Cover
// REPORT-NOT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover
// REPORT: ---
// REPORT-NOT: simple_loops 8 0 100.00% 9 0 100.00% 6 0 100.00%
// REPORT-NOT: conditionals 24 0 100.00% 15 0 100.00% 16 2 87.50%
// REPORT-NOT: early_exits 20 4 80.00% 25 2 92.00% 16 6 62.50%
// REPORT-NOT: jumps 39 12 69.23% 48 2 95.83% 26 9 65.38%
// REPORT-NOT: switches 28 5 82.14% 38 4 89.47% 30 9 70.00%
// REPORT-NOT: big_switch 25 1 96.00% 32 0 100.00% 30 6 80.00%
// REPORT-NOT: boolean_operators 16 0 100.00% 13 0 100.00% 22 2 90.91%
// REPORT-NOT: boolop_loops 19 0 100.00% 14 0 100.00% 16 2 87.50%
// REPORT-NOT: conditional_operator 4 2 50.00% 8 0 100.00% 4 2 50.00%
// REPORT-NOT: do_fallthrough 9 0 100.00% 12 0 100.00% 6 0 100.00%
// REPORT-NOT: main 1 0 100.00% 16 0 100.00% 0 0 0.00%
// REPORT-NOT: c-general.c:static_func 4 0 100.00% 4 0 100.00% 2 0 100.00%
// REPORT: TOTAL 197 24 87.82% 234 13 94.44%
// REPORT-NOT: TOTAL 197 24 87.82% 234 13 94.44% 174 38 78.16%

77 changes: 77 additions & 0 deletions llvm/test/tools/llvm-cov/branch-showBranchPercentage.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Test visualization of branch taken percentages

// RUN: llvm-profdata merge %S/Inputs/branch-showBranchPercentage.proftext -o %t.profdata
// RUN: llvm-cov show --show-branches=percent %S/Inputs/branch-showBranchPercentage.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s

#include <stdio.h>
#include <stdlib.h>

extern void __llvm_profile_write_file(void);

int main(int argc, char *argv[])
{
int i = 0;
if (argc < 3) // CHECK: Branch ([[@LINE]]:7): [True: 16.67%, False: 83.33%]
{
__llvm_profile_write_file();
return 0;
}

int a = atoi(argv[1]);
int b = atoi(argv[2]);

// CHECK: Branch ([[@LINE+4]]:8): [True: 20.00%, False: 80.00%]
// CHECK: Branch ([[@LINE+3]]:18): [True: 0.00%, False: 100.00%]
// CHECK: Branch ([[@LINE+2]]:29): [True: 0.00%, False: 100.00%]
// CHECK: Branch ([[@LINE+1]]:40): [True: 40.00%, False: 60.00%]
if ((a == 0 && b == 2) || b == 34 || a == b)
printf("case1\n");

b = (a != 0 || a == 2) ? b : b+2; // CHECK: Branch ([[@LINE]]:8): [True: 80.00%, False: 20.00%]
// CHECK: Branch ([[@LINE-1]]:18): [True: 0.00%, False: 100.00%]
b = (a != 0 && a == 1); // CHECK: Branch ([[@LINE]]:8): [True: 80.00%, False: 20.00%]
// CHECK: Branch ([[@LINE-1]]:18): [True: 25.00%, False: 75.00%]
for (i = 0; i < b; i++) { a = 2 + b + b; }
// CHECK: Branch ([[@LINE-1]]:15): [True: 16.67%, False: 83.33%]

b = a;

switch (a)
{
case 0: // CHECK: Branch ([[@LINE]]:5): [True: 20.00%, False: 80.00%]
printf("case0\n");
case 2: // CHECK: Branch ([[@LINE]]:5): [True: 20.00%, False: 80.00%]
printf("case2\n");
case 3: // CHECK: Branch ([[@LINE]]:5): [True: 0.00%, False: 100.00%]
printf("case3\n");
default: break; // CHECK: Branch ([[@LINE]]:5): [True: 60.00%, False: 40.00%]
}

i = 0;
do {
printf("loop\n");
} while (i++ < 10); // CHECK: Branch ([[@LINE]]:12): [True: 90.91%, False: 9.09%]

__llvm_profile_write_file();

return b;
}
// RUN: llvm-profdata merge %S/Inputs/branch-showBranchPercentage.proftext -o %t.profdata
// RUN: llvm-cov show --show-branches=percent %S/Inputs/branch-showBranchPercentage.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s -format html -o %t.html.dir

// Test html output.
// RUN: FileCheck -check-prefix=HTML -input-file=%t.html.dir/coverage/tmp/branch-showBranchPercentage.c.html %s
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}16.67%,{{.*}}83.33%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}20.00%,{{.*}}80.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}0.00%,{{.*}}100.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}0.00%,{{.*}}100.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}40.00%,{{.*}}60.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}80.00%,{{.*}}20.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}0.00%,{{.*}}100.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}80.00%,{{.*}}20.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}25.00%,{{.*}}75.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}16.67%,{{.*}}83.33%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}20.00%,{{.*}}80.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}20.00%,{{.*}}80.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}0.00%,{{.*}}100.00%]
// HTML: Branch (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>{{.*}}60.00%,{{.*}}40.00%]
47 changes: 47 additions & 0 deletions llvm/test/tools/llvm-cov/branch-templates.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// RUN: llvm-profdata merge %S/Inputs/branch-templates.proftext -o %t.profdata
// RUN: llvm-cov show --show-expansions --show-branches=count %S/Inputs/branch-templates.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s
// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-templates.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=REPORT

#include <stdio.h>

template<typename T>
void unused(T x) {
return;
}

template<typename T>
int func(T x) {
if(x) // CHECK: | Branch ([[@LINE]]:6): [True: 0, False: 1]
return 0; // CHECK: | Branch ([[@LINE-1]]:6): [True: 1, False: 0]
else // CHECK: | Branch ([[@LINE-2]]:6): [True: 0, False: 1]
return 1;
int j = 1;
}

// CHECK-LABEL: _Z4funcIiEiT_:
// CHECK: | | Branch ([[@LINE-8]]:6): [True: 0, False: 1]
// CHECK-LABEL: _Z4funcIbEiT_:
// CHECK: | | Branch ([[@LINE-10]]:6): [True: 1, False: 0]
// CHECK-LABEL: _Z4funcIfEiT_:
// CHECK: | | Branch ([[@LINE-12]]:6): [True: 0, False: 1]

extern "C" { extern void __llvm_profile_write_file(void); }
int main() {
if (func<int>(0)) // CHECK: | Branch ([[@LINE]]:7): [True: 1, False: 0]
printf("case1\n");
if (func<bool>(true)) // CHECK: | Branch ([[@LINE]]:7): [True: 0, False: 1]
printf("case2\n");
if (func<float>(0.0)) // CHECK: | Branch ([[@LINE]]:7): [True: 1, False: 0]
printf("case3\n");
__llvm_profile_write_file();
return 0;
}

// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover
// REPORT-NEXT: ---
// REPORT-NEXT: main 7 1 85.71% 10 1 90.00% 6 3 50.00%
// REPORT-NEXT: _Z4funcIiEiT_ 5 2 60.00% 7 3 57.14% 2 1 50.00%
// REPORT-NEXT: _Z4funcIbEiT_ 5 2 60.00% 7 4 42.86% 2 1 50.00%
// REPORT-NEXT: _Z4funcIfEiT_ 5 2 60.00% 7 3 57.14% 2 1 50.00%
// REPORT-NEXT: ---
// REPORT-NEXT: TOTAL 22 7 68.18% 31 11 64.52% 12 6 50.00%
6 changes: 3 additions & 3 deletions llvm/test/tools/llvm-cov/ignore-filename-regex.test
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Ignore all header files.
RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \
RUN: -path-equivalence=/tmp,%S/Inputs -ignore-filename-regex='.*\.h$' \
RUN: %S/Inputs/sources_specified/main.covmapping \
RUN: %S/Inputs/sources_specified/main.covmapping --show-branch-summary=false \
RUN: | FileCheck -check-prefix=REPORT_IGNORE_HEADERS %s

REPORT_IGNORE_HEADERS-NOT: {{.*}}dec.h{{.*}}
Expand All @@ -15,7 +15,7 @@ REPORT_IGNORE_HEADERS: {{^}}TOTAL 1{{.*}}100.00%{{$}}
# Ignore all files from "extra" directory.
RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \
RUN: -path-equivalence=/tmp,%S/Inputs -ignore-filename-regex='.*extra[/\\].*' \
RUN: %S/Inputs/sources_specified/main.covmapping \
RUN: %S/Inputs/sources_specified/main.covmapping --show-branch-summary=false \
RUN: | FileCheck -check-prefix=REPORT_IGNORE_DIR %s

REPORT_IGNORE_DIR-NOT: {{.*}}extra{{[/\\]}}dec.h{{.*}}
Expand All @@ -27,7 +27,7 @@ REPORT_IGNORE_DIR: {{^}}TOTAL 5{{.*}}90.00%{{$}}
# Ignore all files from "extra" directory even when SOURCES specified.
RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \
RUN: -path-equivalence=/tmp,%S/Inputs -ignore-filename-regex='.*extra[/\\].*' \
RUN: %S/Inputs/sources_specified/main.covmapping \
RUN: %S/Inputs/sources_specified/main.covmapping --show-branch-summary=false \
RUN: %S/Inputs/sources_specified/extra %S/Inputs/sources_specified/abs.h \
RUN: | FileCheck -check-prefix=REPORT_IGNORE_DIR_WITH_SOURCES %s

Expand Down
64 changes: 64 additions & 0 deletions llvm/tools/llvm-cov/CodeCoverage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ class CodeCoverageTool {
ArrayRef<ExpansionRecord> Expansions,
const CoverageMapping &Coverage);

/// Create source views for the branches of the view.
void attachBranchSubViews(SourceCoverageView &View, StringRef SourceName,
ArrayRef<CountedRegion> Branches,
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 @@ -268,15 +274,45 @@ void CodeCoverageTool::attachExpansionSubViews(
if (!SourceBuffer)
continue;

auto SubViewBranches = ExpansionCoverage.getBranches();
auto SubViewExpansions = ExpansionCoverage.getExpansions();
auto SubView =
SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
ViewOpts, std::move(ExpansionCoverage));
attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
attachBranchSubViews(*SubView, Expansion.Function.Name, SubViewBranches,
SourceBuffer.get(), ExpansionCoverage);
View.addExpansion(Expansion.Region, std::move(SubView));
}
}

void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View,
StringRef SourceName,
ArrayRef<CountedRegion> Branches,
const MemoryBuffer &File,
CoverageData &CoverageInfo) {
if (!ViewOpts.ShowBranchCounts && !ViewOpts.ShowBranchPercents)
return;

const auto *NextBranch = Branches.begin();
const auto *EndBranch = Branches.end();

// Group branches that have the same line number into the same subview.
while (NextBranch != EndBranch) {
std::vector<CountedRegion> ViewBranches;
unsigned CurrentLine = NextBranch->LineStart;

while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart)
ViewBranches.push_back(*NextBranch++);

if (!ViewBranches.empty()) {
auto SubView = SourceCoverageView::create(SourceName, File, ViewOpts,
std::move(CoverageInfo));
View.addBranch(CurrentLine, ViewBranches, std::move(SubView));
}
}
}

std::unique_ptr<SourceCoverageView>
CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
const CoverageMapping &Coverage) {
Expand All @@ -287,11 +323,14 @@ CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
if (!SourceBuffer)
return nullptr;

auto Branches = FunctionCoverage.getBranches();
auto Expansions = FunctionCoverage.getExpansions();
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);

return View;
}
Expand All @@ -306,10 +345,13 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile,
if (FileCoverage.empty())
return nullptr;

auto Branches = FileCoverage.getBranches();
auto Expansions = FileCoverage.getExpansions();
auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
ViewOpts, std::move(FileCoverage));
attachExpansionSubViews(*View, Expansions, Coverage);
attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(),
FileCoverage);
if (!ViewOpts.ShowFunctionInstantiations)
return View;

Expand All @@ -326,9 +368,12 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile,
if (Function->ExecutionCount > 0) {
auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
auto SubViewExpansions = SubViewCoverage.getExpansions();
auto SubViewBranches = SubViewCoverage.getBranches();
SubView = SourceCoverageView::create(
Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
attachBranchSubViews(*SubView, SourceFile, SubViewBranches,
SourceBuffer.get(), SubViewCoverage);
}

unsigned FileID = Function->CountedRegions.front().FileID;
Expand Down Expand Up @@ -645,6 +690,11 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
cl::desc("Show region statistics in summary table"),
cl::init(true));

cl::opt<bool> BranchSummary(
"show-branch-summary", cl::Optional,
cl::desc("Show branch condition statistics in summary table"),
cl::init(true));

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

ViewOpts.ShowBranchSummary = BranchSummary;
ViewOpts.ShowRegionSummary = RegionSummary;
ViewOpts.ShowInstantiationSummary = InstantiationSummary;
ViewOpts.ExportSummaryOnly = SummaryOnly;
Expand Down Expand Up @@ -822,6 +873,15 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
cl::desc("Show the execution counts for each region"),
cl::cat(ViewCategory));

cl::opt<CoverageViewOptions::BranchOutputType> ShowBranches(
"show-branches", cl::Optional,
cl::desc("Show coverage for branch conditions"), cl::cat(ViewCategory),
cl::values(clEnumValN(CoverageViewOptions::BranchOutputType::Count,
"count", "Show True/False counts"),
clEnumValN(CoverageViewOptions::BranchOutputType::Percent,
"percent", "Show True/False percent")),
cl::init(CoverageViewOptions::BranchOutputType::Off));

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 @@ -865,6 +925,10 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
!ShowRegions || ShowBestLineRegionsCounts;
ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
ViewOpts.ShowExpandedRegions = ShowExpansions;
ViewOpts.ShowBranchCounts =
ShowBranches == CoverageViewOptions::BranchOutputType::Count;
ViewOpts.ShowBranchPercents =
ShowBranches == CoverageViewOptions::BranchOutputType::Percent;
ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
ViewOpts.TabSize = TabSize;
Expand Down
81 changes: 75 additions & 6 deletions llvm/tools/llvm-cov/CoverageExporterJson.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,22 @@
// -- Export: dict => Json representation of one CoverageMapping
// -- Files: array => List of objects describing coverage for files
// -- 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
// -- 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
// -- Expansion: dict => Object that descibes a single expansion
// -- CountedRegion: dict => The region to be expanded
// -- TargetRegions: array => List of Regions in the expansion
// -- CountedRegion: dict => Single Region in the expansion
// -- Branches: array => List of Branches in the expansion
// -- Branch: dict => Describes a branch in expansion and counters
// -- Summary: dict => Object summarizing the coverage for this file
// -- LineCoverage: dict => Object summarizing line coverage
// -- FunctionCoverage: dict => Object summarizing function coverage
// -- RegionCoverage: dict => Object summarizing region coverage
// -- BranchCoverage: dict => Object summarizing branch 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 @@ -37,6 +42,7 @@
// -- FunctionCoverage: dict => Object summarizing function coverage
// -- InstantiationCoverage: dict => Object summarizing inst. coverage
// -- RegionCoverage: dict => Object summarizing region coverage
// -- BranchCoverage: dict => Object summarizing branch coverage
//
//===----------------------------------------------------------------------===//

Expand Down Expand Up @@ -84,20 +90,64 @@ json::Array renderRegion(const coverage::CountedRegion &Region) {
int64_t(Region.Kind)});
}

json::Array renderBranch(const coverage::CountedRegion &Region) {
return json::Array(
{Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd,
clamp_uint64_to_int64(Region.ExecutionCount),
clamp_uint64_to_int64(Region.FalseExecutionCount), Region.FileID,
Region.ExpandedFileID, int64_t(Region.Kind)});
}

json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) {
json::Array RegionArray;
for (const auto &Region : Regions)
RegionArray.push_back(renderRegion(Region));
return RegionArray;
}

json::Object renderExpansion(const coverage::ExpansionRecord &Expansion) {
json::Array renderBranchRegions(ArrayRef<coverage::CountedRegion> Regions) {
json::Array RegionArray;
for (const auto &Region : Regions)
if (!Region.Folded)
RegionArray.push_back(renderBranch(Region));
return RegionArray;
}

std::vector<llvm::coverage::CountedRegion>
collectNestedBranches(const coverage::CoverageMapping &Coverage,
ArrayRef<llvm::coverage::ExpansionRecord> Expansions) {
std::vector<llvm::coverage::CountedRegion> Branches;
for (const auto &Expansion : Expansions) {
auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);

// Recursively collect branches from nested expansions.
auto NestedExpansions = ExpansionCoverage.getExpansions();
auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions);
Branches.insert(Branches.end(), NestedExBranches.begin(),
NestedExBranches.end());

// Add branches from this level of expansion.
auto ExBranches = ExpansionCoverage.getBranches();
for (auto B : ExBranches)
if (B.FileID == Expansion.FileID)
Branches.push_back(B);
}

return Branches;
}

json::Object renderExpansion(const coverage::CoverageMapping &Coverage,
const coverage::ExpansionRecord &Expansion) {
std::vector<llvm::coverage::ExpansionRecord> Expansions = {Expansion};
return json::Object(
{{"filenames", json::Array(Expansion.Function.Filenames)},
// Mark the beginning and end of this expansion in the source file.
{"source_region", renderRegion(Expansion.Region)},
// Enumerate the coverage information for the expansion.
{"target_regions", renderRegions(Expansion.Function.CountedRegions)}});
{"target_regions", renderRegions(Expansion.Function.CountedRegions)},
// Enumerate the branch coverage information for the expansion.
{"branches",
renderBranchRegions(collectNestedBranches(Coverage, Expansions))}});
}

json::Object renderSummary(const FileCoverageSummary &Summary) {
Expand All @@ -123,14 +173,22 @@ json::Object renderSummary(const FileCoverageSummary &Summary) {
{"covered", int64_t(Summary.RegionCoverage.getCovered())},
{"notcovered", int64_t(Summary.RegionCoverage.getNumRegions() -
Summary.RegionCoverage.getCovered())},
{"percent", Summary.RegionCoverage.getPercentCovered()}})}});
{"percent", Summary.RegionCoverage.getPercentCovered()}})},
{"branches",
json::Object(
{{"count", int64_t(Summary.BranchCoverage.getNumBranches())},
{"covered", int64_t(Summary.BranchCoverage.getCovered())},
{"notcovered", int64_t(Summary.BranchCoverage.getNumBranches() -
Summary.BranchCoverage.getCovered())},
{"percent", Summary.BranchCoverage.getPercentCovered()}})}});
}

json::Array renderFileExpansions(const coverage::CoverageData &FileCoverage,
json::Array renderFileExpansions(const coverage::CoverageMapping &Coverage,
const coverage::CoverageData &FileCoverage,
const FileCoverageSummary &FileReport) {
json::Array ExpansionArray;
for (const auto &Expansion : FileCoverage.getExpansions())
ExpansionArray.push_back(renderExpansion(Expansion));
ExpansionArray.push_back(renderExpansion(Coverage, Expansion));
return ExpansionArray;
}

Expand All @@ -142,6 +200,14 @@ json::Array renderFileSegments(const coverage::CoverageData &FileCoverage,
return SegmentArray;
}

json::Array renderFileBranches(const coverage::CoverageData &FileCoverage,
const FileCoverageSummary &FileReport) {
json::Array BranchArray;
for (const auto &Branch : FileCoverage.getBranches())
BranchArray.push_back(renderBranch(Branch));
return BranchArray;
}

json::Object renderFile(const coverage::CoverageMapping &Coverage,
const std::string &Filename,
const FileCoverageSummary &FileReport,
Expand All @@ -151,8 +217,10 @@ json::Object renderFile(const coverage::CoverageMapping &Coverage,
// Calculate and render detailed coverage information for given file.
auto FileCoverage = Coverage.getCoverageForFile(Filename);
File["segments"] = renderFileSegments(FileCoverage, FileReport);
File["branches"] = renderFileBranches(FileCoverage, FileReport);
if (!Options.SkipExpansions) {
File["expansions"] = renderFileExpansions(FileCoverage, FileReport);
File["expansions"] =
renderFileExpansions(Coverage, FileCoverage, FileReport);
}
}
File["summary"] = renderSummary(FileReport);
Expand Down Expand Up @@ -197,6 +265,7 @@ json::Array renderFunctions(
json::Object({{"name", F.Name},
{"count", clamp_uint64_to_int64(F.ExecutionCount)},
{"regions", renderRegions(F.CountedRegions)},
{"branches", renderBranchRegions(F.CountedBranchRegions)},
{"filenames", json::Array(F.Filenames)}}));
return FunctionArray;
}
Expand Down
97 changes: 97 additions & 0 deletions llvm/tools/llvm-cov/CoverageExporterLcov.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
// - "FNH:<number of functions hit>"
// - for each instrumented line:
// - "DA:<line number>,<execution count>[,<checksum>]
// - for each branch:
// - "BRDA:<line number>,<branch pair id>,<branch id>,<count>"
// - "BRF:<number of branches found>"
// - "BRH:<number of branches hit>"
// - "LH:<number of lines with non-zero execution count>"
// - "LF:<number of instrumented lines>"
// - "end_of_record"
Expand Down Expand Up @@ -71,11 +75,102 @@ void renderLineExecutionCounts(raw_ostream &OS,
}
}

std::vector<llvm::coverage::CountedRegion>
collectNestedBranches(const coverage::CoverageMapping &Coverage,
ArrayRef<llvm::coverage::ExpansionRecord> Expansions,
int ViewDepth = 0, int SrcLine = 0) {
std::vector<llvm::coverage::CountedRegion> Branches;
for (const auto &Expansion : Expansions) {
auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);

// If we're at the top level, set the corresponding source line.
if (ViewDepth == 0)
SrcLine = Expansion.Region.LineStart;

// Recursively collect branches from nested expansions.
auto NestedExpansions = ExpansionCoverage.getExpansions();
auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions,
ViewDepth + 1, SrcLine);
Branches.insert(Branches.end(), NestedExBranches.begin(),
NestedExBranches.end());

// Add branches from this level of expansion.
auto ExBranches = ExpansionCoverage.getBranches();
for (auto B : ExBranches)
if (B.FileID == Expansion.FileID) {
B.LineStart = SrcLine;
Branches.push_back(B);
}
}

return Branches;
}

bool sortLine(llvm::coverage::CountedRegion I,
llvm::coverage::CountedRegion J) {
return (I.LineStart < J.LineStart) ||
((I.LineStart == J.LineStart) && (I.ColumnStart < J.ColumnStart));
}

void renderBranchExecutionCounts(raw_ostream &OS,
const coverage::CoverageMapping &Coverage,
const coverage::CoverageData &FileCoverage) {
std::vector<llvm::coverage::CountedRegion> Branches =
FileCoverage.getBranches();

// Recursively collect branches for all file expansions.
std::vector<llvm::coverage::CountedRegion> ExBranches =
collectNestedBranches(Coverage, FileCoverage.getExpansions());

// Append Expansion Branches to Source Branches.
Branches.insert(Branches.end(), ExBranches.begin(), ExBranches.end());

// Sort branches based on line number to ensure branches corresponding to the
// same source line are counted together.
std::sort(Branches.begin(), Branches.end(), sortLine);

auto NextBranch = Branches.begin();
auto EndBranch = Branches.end();

// Branches with the same source line are enumerated individually
// (BranchIndex) as well as based on True/False pairs (PairIndex).
while (NextBranch != EndBranch) {
unsigned CurrentLine = NextBranch->LineStart;
unsigned PairIndex = 0;
unsigned BranchIndex = 0;

while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) {
if (!NextBranch->Folded) {
unsigned BC1 = NextBranch->ExecutionCount;
unsigned BC2 = NextBranch->FalseExecutionCount;
bool BranchNotExecuted = (BC1 == 0 && BC2 == 0);

for (int I = 0; I < 2; I++, BranchIndex++) {
OS << "BRDA:" << CurrentLine << ',' << PairIndex << ','
<< BranchIndex;
if (BranchNotExecuted)
OS << ',' << '-' << '\n';
else
OS << ',' << (I == 0 ? BC1 : BC2) << '\n';
}

PairIndex++;
}
NextBranch++;
}
}
}

void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n'
<< "LH:" << Summary.LineCoverage.getCovered() << '\n';
}

void renderBranchSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
OS << "BRF:" << Summary.BranchCoverage.getNumBranches() << '\n'
<< "BFH:" << Summary.BranchCoverage.getCovered() << '\n';
}

void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
const std::string &Filename,
const FileCoverageSummary &FileReport, bool ExportSummaryOnly,
Expand All @@ -91,7 +186,9 @@ void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
// Calculate and render detailed coverage information for given file.
auto FileCoverage = Coverage.getCoverageForFile(Filename);
renderLineExecutionCounts(OS, FileCoverage);
renderBranchExecutionCounts(OS, Coverage, FileCoverage);
}
renderBranchSummary(OS, FileReport);
renderLineSummary(OS, FileReport);

OS << "end_of_record\n";
Expand Down
49 changes: 45 additions & 4 deletions llvm/tools/llvm-cov/CoverageReport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ Column column(StringRef Str, unsigned Width, const T &Value) {
}

// Specify the default column widths.
size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10,
16, 16, 10, 12, 18, 10};
size_t FunctionReportColumns[] = {25, 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};
size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8, 10, 8, 8};

/// Adjust column widths to fit long file paths and function names.
void adjustColumnWidths(ArrayRef<StringRef> Files,
Expand Down Expand Up @@ -239,6 +239,23 @@ void CoverageReport::render(const FileCoverageSummary &File,
<< '%';
else
OS << column("-", FileReportColumns[12], Column::RightAlignment);

if (Options.ShowBranchSummary) {
OS << format("%*u", FileReportColumns[13],
(unsigned)File.BranchCoverage.getNumBranches());
Options.colored_ostream(OS, LineCoverageColor)
<< format("%*u", FileReportColumns[14],
(unsigned)(File.BranchCoverage.getNumBranches() -
File.BranchCoverage.getCovered()));
if (File.BranchCoverage.getNumBranches())
Options.colored_ostream(OS, LineCoverageColor)
<< format("%*.2f", FileReportColumns[15] - 1,
File.BranchCoverage.getPercentCovered())
<< '%';
else
OS << column("-", FileReportColumns[15], Column::RightAlignment);
}

OS << "\n";
}

Expand Down Expand Up @@ -273,6 +290,19 @@ void CoverageReport::render(const FunctionCoverageSummary &Function,
<< format("%*.2f", FunctionReportColumns[6] - 1,
Function.LineCoverage.getPercentCovered())
<< '%';
if (Options.ShowBranchSummary) {
OS << format("%*u", FunctionReportColumns[7],
(unsigned)Function.BranchCoverage.getNumBranches());
Options.colored_ostream(OS, LineCoverageColor)
<< format("%*u", FunctionReportColumns[8],
(unsigned)(Function.BranchCoverage.getNumBranches() -
Function.BranchCoverage.getCovered()));
Options.colored_ostream(
OS, determineCoveragePercentageColor(Function.BranchCoverage))
<< format("%*.2f", FunctionReportColumns[9] - 1,
Function.BranchCoverage.getPercentCovered())
<< '%';
}
OS << "\n";
}

Expand Down Expand Up @@ -301,6 +331,10 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
<< column("Lines", FunctionReportColumns[4], Column::RightAlignment)
<< column("Miss", FunctionReportColumns[5], Column::RightAlignment)
<< column("Cover", FunctionReportColumns[6], Column::RightAlignment);
if (Options.ShowBranchSummary)
OS << column("Branches", FunctionReportColumns[7], Column::RightAlignment)
<< column("Miss", FunctionReportColumns[8], Column::RightAlignment)
<< column("Cover", FunctionReportColumns[9], Column::RightAlignment);
OS << "\n";
renderDivider(FunctionReportColumns, OS);
OS << "\n";
Expand All @@ -310,6 +344,7 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
++Totals.ExecutionCount;
Totals.RegionCoverage += Function.RegionCoverage;
Totals.LineCoverage += Function.LineCoverage;
Totals.BranchCoverage += Function.BranchCoverage;
render(Function, DC, OS);
}
if (Totals.ExecutionCount) {
Expand Down Expand Up @@ -420,7 +455,13 @@ void CoverageReport::renderFileReports(
<< column("Executed", FileReportColumns[9], Column::RightAlignment);
OS << column("Lines", FileReportColumns[10], Column::RightAlignment)
<< column("Missed Lines", FileReportColumns[11], Column::RightAlignment)
<< column("Cover", FileReportColumns[12], Column::RightAlignment) << "\n";
<< column("Cover", FileReportColumns[12], Column::RightAlignment);
if (Options.ShowBranchSummary)
OS << column("Branches", FileReportColumns[13], Column::RightAlignment)
<< column("Missed Branches", FileReportColumns[14],
Column::RightAlignment)
<< column("Cover", FileReportColumns[15], Column::RightAlignment);
OS << "\n";
renderDivider(FileReportColumns, OS);
OS << "\n";

Expand Down
42 changes: 41 additions & 1 deletion llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,34 @@
using namespace llvm;
using namespace coverage;

static void sumBranches(size_t &NumBranches, size_t &CoveredBranches,
const ArrayRef<CountedRegion> &Branches) {
for (const auto &BR : Branches) {
// Skip folded branches.
if (BR.Folded)
continue;

// "True" Condition Branches.
++NumBranches;
if (BR.ExecutionCount > 0)
++CoveredBranches;
// "False" Condition Branches.
++NumBranches;
if (BR.FalseExecutionCount > 0)
++CoveredBranches;
}
}

static void sumBranchExpansions(size_t &NumBranches, size_t &CoveredBranches,
const CoverageMapping &CM,
ArrayRef<ExpansionRecord> Expansions) {
for (const auto &Expansion : Expansions) {
auto CE = CM.getCoverageForExpansion(Expansion);
sumBranches(NumBranches, CoveredBranches, CE.getBranches());
sumBranchExpansions(NumBranches, CoveredBranches, CM, CE.getExpansions());
}
}

FunctionCoverageSummary
FunctionCoverageSummary::get(const CoverageMapping &CM,
const coverage::FunctionRecord &Function) {
Expand All @@ -40,10 +68,16 @@ FunctionCoverageSummary::get(const CoverageMapping &CM,
++CoveredLines;
}

// Compute the branch coverage, including branches from expansions.
size_t NumBranches = 0, CoveredBranches = 0;
sumBranches(NumBranches, CoveredBranches, CD.getBranches());
sumBranchExpansions(NumBranches, CoveredBranches, CM, CD.getExpansions());

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

FunctionCoverageSummary
Expand All @@ -62,9 +96,15 @@ FunctionCoverageSummary::get(const InstantiationGroup &Group,
Summary.ExecutionCount = Group.getTotalExecutionCount();
Summary.RegionCoverage = Summaries[0].RegionCoverage;
Summary.LineCoverage = Summaries[0].LineCoverage;
Summary.BranchCoverage = Summaries[0].BranchCoverage;
for (const auto &FCS : Summaries.drop_front()) {
Summary.RegionCoverage.merge(FCS.RegionCoverage);
Summary.LineCoverage.merge(FCS.LineCoverage);

// Sum branch coverage across instantiation groups for the summary rather
// than "merge" the maximum count. This is a clearer view into whether all
// created branches are covered.
Summary.BranchCoverage += FCS.BranchCoverage;
}
return Summary;
}
49 changes: 46 additions & 3 deletions llvm/tools/llvm-cov/CoverageSummaryInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,42 @@ class LineCoverageInfo {
}
};

/// Provides information about branches coverage for a function/file.
class BranchCoverageInfo {
/// The number of branches that were executed at least once.
size_t Covered;

/// The total number of branches in a function/file.
size_t NumBranches;

public:
BranchCoverageInfo() : Covered(0), NumBranches(0) {}

BranchCoverageInfo(size_t Covered, size_t NumBranches)
: Covered(Covered), NumBranches(NumBranches) {
assert(Covered <= NumBranches && "Covered branches over-counted");
}

BranchCoverageInfo &operator+=(const BranchCoverageInfo &RHS) {
Covered += RHS.Covered;
NumBranches += RHS.NumBranches;
return *this;
}

size_t getCovered() const { return Covered; }

size_t getNumBranches() const { return NumBranches; }

bool isFullyCovered() const { return Covered == NumBranches; }

double getPercentCovered() const {
assert(Covered <= NumBranches && "Covered branches over-counted");
if (NumBranches == 0)
return 0.0;
return double(Covered) / double(NumBranches) * 100.0;
}
};

/// Provides information about function coverage for a file.
class FunctionCoverageInfo {
/// The number of functions that were executed.
Expand Down Expand Up @@ -147,15 +183,19 @@ struct FunctionCoverageSummary {
uint64_t ExecutionCount;
RegionCoverageInfo RegionCoverage;
LineCoverageInfo LineCoverage;
BranchCoverageInfo BranchCoverage;

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

FunctionCoverageSummary(const std::string &Name, uint64_t ExecutionCount,
const RegionCoverageInfo &RegionCoverage,
const LineCoverageInfo &LineCoverage)
const LineCoverageInfo &LineCoverage,
const BranchCoverageInfo &BranchCoverage)
: Name(Name), ExecutionCount(ExecutionCount),
RegionCoverage(RegionCoverage), LineCoverage(LineCoverage) {}
RegionCoverage(RegionCoverage), LineCoverage(LineCoverage),
BranchCoverage(BranchCoverage) {}

/// Compute the code coverage summary for the given function coverage
/// mapping record.
Expand All @@ -174,6 +214,7 @@ struct FileCoverageSummary {
StringRef Name;
RegionCoverageInfo RegionCoverage;
LineCoverageInfo LineCoverage;
BranchCoverageInfo BranchCoverage;
FunctionCoverageInfo FunctionCoverage;
FunctionCoverageInfo InstantiationCoverage;

Expand All @@ -185,13 +226,15 @@ struct FileCoverageSummary {
RegionCoverage += RHS.RegionCoverage;
LineCoverage += RHS.LineCoverage;
FunctionCoverage += RHS.FunctionCoverage;
BranchCoverage += RHS.BranchCoverage;
InstantiationCoverage += RHS.InstantiationCoverage;
return *this;
}

void addFunction(const FunctionCoverageSummary &Function) {
RegionCoverage += Function.RegionCoverage;
LineCoverage += Function.LineCoverage;
BranchCoverage += Function.BranchCoverage;
FunctionCoverage.addFunction(/*Covered=*/Function.ExecutionCount > 0);
}

Expand Down
6 changes: 6 additions & 0 deletions llvm/tools/llvm-cov/CoverageViewOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,26 @@ struct CoverageViewOptions {
Lcov
};

enum class BranchOutputType { Count, Percent, Off };

bool Debug;
bool Colors;
bool ShowLineNumbers;
bool ShowLineStats;
bool ShowRegionMarkers;
bool ShowBranchCounts;
bool ShowBranchPercents;
bool ShowExpandedRegions;
bool ShowFunctionInstantiations;
bool ShowFullFilenames;
bool ShowBranchSummary;
bool ShowRegionSummary;
bool ShowInstantiationSummary;
bool ExportSummaryOnly;
bool SkipExpansions;
bool SkipFunctions;
OutputFormat Format;
BranchOutputType ShowBranches;
std::string ShowOutputDirectory;
std::vector<std::string> DemanglerOpts;
uint32_t TabSize;
Expand Down
23 changes: 19 additions & 4 deletions llvm/tools/llvm-cov/SourceCoverageView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ bool SourceCoverageView::shouldRenderRegionMarkers(
}

bool SourceCoverageView::hasSubViews() const {
return !ExpansionSubViews.empty() || !InstantiationSubViews.empty();
return !ExpansionSubViews.empty() || !InstantiationSubViews.empty() ||
!BranchSubViews.empty();
}

std::unique_ptr<SourceCoverageView>
Expand Down Expand Up @@ -167,6 +168,12 @@ void SourceCoverageView::addExpansion(
ExpansionSubViews.emplace_back(Region, std::move(View));
}

void SourceCoverageView::addBranch(unsigned Line,
ArrayRef<CountedRegion> Regions,
std::unique_ptr<SourceCoverageView> View) {
BranchSubViews.emplace_back(Line, Regions, std::move(View));
}

void SourceCoverageView::addInstantiation(
StringRef FunctionName, unsigned Line,
std::unique_ptr<SourceCoverageView> View) {
Expand All @@ -187,14 +194,17 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
renderTableHeader(OS, (ViewDepth > 0) ? 0 : getFirstUncoveredLineNo(),
ViewDepth);

// We need the expansions and instantiations sorted so we can go through them
// while we iterate lines.
// We need the expansions, instantiations, and branches sorted so we can go
// through them while we iterate lines.
llvm::stable_sort(ExpansionSubViews);
llvm::stable_sort(InstantiationSubViews);
llvm::stable_sort(BranchSubViews);
auto NextESV = ExpansionSubViews.begin();
auto EndESV = ExpansionSubViews.end();
auto NextISV = InstantiationSubViews.begin();
auto EndISV = InstantiationSubViews.end();
auto NextBRV = BranchSubViews.begin();
auto EndBRV = BranchSubViews.end();

// Get the coverage information for the file.
auto StartSegment = CoverageInfo.begin();
Expand Down Expand Up @@ -234,7 +244,7 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
if (shouldRenderRegionMarkers(*LCI))
renderRegionMarkers(OS, *LCI, ViewDepth);

// Show the expansions and instantiations for this line.
// Show the expansions, instantiations, and branches for this line.
bool RenderedSubView = false;
for (; NextESV != EndESV && NextESV->getLine() == LI.line_number();
++NextESV) {
Expand All @@ -257,6 +267,11 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
renderInstantiationView(OS, *NextISV, ViewDepth + 1);
RenderedSubView = true;
}
for (; NextBRV != EndBRV && NextBRV->Line == LI.line_number(); ++NextBRV) {
renderViewDivider(OS, ViewDepth + 1);
renderBranchView(OS, *NextBRV, 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 @@ -67,6 +67,23 @@ struct InstantiationView {
}
};

/// A view that represents one or more branch regions on a given source line.
struct BranchView {
std::vector<CountedRegion> Regions;
std::unique_ptr<SourceCoverageView> View;
unsigned Line;

BranchView(unsigned Line, ArrayRef<CountedRegion> Regions,
std::unique_ptr<SourceCoverageView> View)
: Regions(Regions), View(std::move(View)), Line(Line) {}

unsigned getLine() const { return Line; }

friend bool operator<(const BranchView &LHS, const BranchView &RHS) {
return LHS.Line < RHS.Line;
}
};

/// A file manager that handles format-aware file creation.
class CoveragePrinter {
public:
Expand Down Expand Up @@ -140,6 +157,9 @@ class SourceCoverageView {
/// A container for all expansions (e.g macros) in the source on display.
std::vector<ExpansionView> ExpansionSubViews;

/// A container for all branches in the source on display.
std::vector<BranchView> BranchSubViews;

/// A container for all instantiations (e.g template functions) in the source
/// on display.
std::vector<InstantiationView> InstantiationSubViews;
Expand Down Expand Up @@ -209,6 +229,10 @@ class SourceCoverageView {
virtual void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
unsigned ViewDepth) = 0;

/// Render a branch view and any nested views.
virtual void renderBranchView(raw_ostream &OS, BranchView &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 @@ -255,6 +279,10 @@ class SourceCoverageView {
void addInstantiation(StringRef FunctionName, unsigned Line,
std::unique_ptr<SourceCoverageView> View);

/// Add a branch subview to this view.
void addBranch(unsigned Line, ArrayRef<CountedRegion> Regions,
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
72 changes: 72 additions & 0 deletions llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ static void emitColumnLabelsForIndex(raw_ostream &OS,
Columns.emplace_back(tag("td", "Line Coverage", "column-entry-bold"));
if (Opts.ShowRegionSummary)
Columns.emplace_back(tag("td", "Region Coverage", "column-entry-bold"));
if (Opts.ShowBranchSummary)
Columns.emplace_back(tag("td", "Branch Coverage", "column-entry-bold"));
OS << tag("tr", join(Columns.begin(), Columns.end(), ""));
}

Expand Down Expand Up @@ -378,6 +380,10 @@ void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF,
AddCoverageTripleToColumn(FCS.RegionCoverage.getCovered(),
FCS.RegionCoverage.getNumRegions(),
FCS.RegionCoverage.getPercentCovered());
if (Opts.ShowBranchSummary)
AddCoverageTripleToColumn(FCS.BranchCoverage.getCovered(),
FCS.BranchCoverage.getNumBranches(),
FCS.BranchCoverage.getPercentCovered());

if (IsTotals)
OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row-bold");
Expand Down Expand Up @@ -650,6 +656,72 @@ void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS,
OS << EndExpansionDiv;
}

void SourceCoverageViewHTML::renderBranchView(raw_ostream &OS, BranchView &BRV,
unsigned ViewDepth) {
// Render the child subview.
if (getOptions().Debug)
errs() << "Branch at line " << BRV.getLine() << '\n';

OS << BeginExpansionDiv;
OS << BeginPre;
for (const auto &R : BRV.Regions) {
// Calculate TruePercent and False Percent.
double TruePercent = 0.0;
double FalsePercent = 0.0;
unsigned Total = R.ExecutionCount + R.FalseExecutionCount;

if (!getOptions().ShowBranchCounts && Total != 0) {
TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0;
FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0;
}

// Display Line + Column.
std::string LineNoStr = utostr(uint64_t(R.LineStart));
std::string ColNoStr = utostr(uint64_t(R.ColumnStart));
std::string TargetName = "L" + LineNoStr;

OS << " Branch (";
OS << tag("span",
a("#" + TargetName, tag("span", LineNoStr + ":" + ColNoStr),
TargetName),
"line-number") +
"): [";

if (R.Folded) {
OS << "Folded - Ignored]\n";
continue;
}

// Display TrueCount or TruePercent.
std::string TrueColor = R.ExecutionCount ? "None" : "red";
std::string TrueCovClass =
(R.ExecutionCount > 0) ? "covered-line" : "uncovered-line";

OS << tag("span", "True", TrueColor);
OS << ": ";
if (getOptions().ShowBranchCounts)
OS << tag("span", formatCount(R.ExecutionCount), TrueCovClass) << ", ";
else
OS << format("%0.2f", TruePercent) << "%, ";

// Display FalseCount or FalsePercent.
std::string FalseColor = R.FalseExecutionCount ? "None" : "red";
std::string FalseCovClass =
(R.FalseExecutionCount > 0) ? "covered-line" : "uncovered-line";

OS << tag("span", "False", FalseColor);
OS << ": ";
if (getOptions().ShowBranchCounts)
OS << tag("span", formatCount(R.FalseExecutionCount), FalseCovClass);
else
OS << format("%0.2f", FalsePercent) << "%";

OS << "]\n";
}
OS << EndPre;
OS << EndExpansionDiv;
}

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 @@ -68,6 +68,9 @@ class SourceCoverageViewHTML : public SourceCoverageView {
void renderExpansionView(raw_ostream &OS, ExpansionView &ESV,
unsigned ViewDepth) override;

void renderBranchView(raw_ostream &OS, BranchView &BRV,
unsigned ViewDepth) override;

void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
unsigned ViewDepth) override;

Expand Down
50 changes: 49 additions & 1 deletion llvm/tools/llvm-cov/SourceCoverageViewText.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
///
//===----------------------------------------------------------------------===//

#include "CoverageReport.h"
#include "SourceCoverageViewText.h"
#include "CoverageReport.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Format.h"

using namespace llvm;

Expand Down Expand Up @@ -222,6 +223,53 @@ void SourceCoverageViewText::renderExpansionView(raw_ostream &OS,
/*ShowTitle=*/false, ViewDepth + 1);
}

void SourceCoverageViewText::renderBranchView(raw_ostream &OS, BranchView &BRV,
unsigned ViewDepth) {
// Render the child subview.
if (getOptions().Debug)
errs() << "Branch at line " << BRV.getLine() << '\n';

for (const auto &R : BRV.Regions) {
double TruePercent = 0.0;
double FalsePercent = 0.0;
unsigned Total = R.ExecutionCount + R.FalseExecutionCount;

if (!getOptions().ShowBranchCounts && Total != 0) {
TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0;
FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0;
}

renderLinePrefix(OS, ViewDepth);
OS << " Branch (" << R.LineStart << ":" << R.ColumnStart << "): [";

if (R.Folded) {
OS << "Folded - Ignored]\n";
continue;
}

colored_ostream(OS, raw_ostream::RED,
getOptions().Colors && !R.ExecutionCount,
/*Bold=*/false, /*BG=*/true)
<< "True";

if (getOptions().ShowBranchCounts)
OS << ": " << formatCount(R.ExecutionCount) << ", ";
else
OS << ": " << format("%0.2f", TruePercent) << "%, ";

colored_ostream(OS, raw_ostream::RED,
getOptions().Colors && !R.FalseExecutionCount,
/*Bold=*/false, /*BG=*/true)
<< "False";

if (getOptions().ShowBranchCounts)
OS << ": " << formatCount(R.FalseExecutionCount);
else
OS << ": " << format("%0.2f", FalsePercent) << "%";
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 @@ -59,6 +59,9 @@ class SourceCoverageViewText : public SourceCoverageView {
void renderExpansionView(raw_ostream &OS, ExpansionView &ESV,
unsigned ViewDepth) override;

void renderBranchView(raw_ostream &OS, BranchView &BRV,
unsigned ViewDepth) override;

void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
unsigned ViewDepth) override;

Expand Down