Skip to content

Commit

Permalink
Implement LcovMerger.
Browse files Browse the repository at this point in the history
LcovMerger is a tool that merges all the intermediate lcov tracefiles (with .dat extension) found under a coverage directory and prints the merged tracefile to a given output file.

A custom implementation for merging lcov tracefiles is needed because the merging functionality of lcov itself is very slow.

LcovMerger is required to get a single coverage report (lcov tracefile) from a bazel coverage command that executes multiple tests.

ATM LcovMerger is only invoked by tools/test/collect_coverage.sh that collects and merges the tracefiles from a single test invocation. It will also be used from a CoverageReportAction.

Progress on bazelbuild#5246.

PiperOrigin-RevId: 200054506
  • Loading branch information
iirina authored and George Gensure committed Aug 2, 2018
1 parent 9dd2bff commit b00c8e7
Show file tree
Hide file tree
Showing 25 changed files with 2,507 additions and 196 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env)
// Input files for test actions collecting code coverage
.add(
attr("$lcov_merger", LABEL)
.value(env.getLabel("@bazel_tools//tools/test:LcovMerger")))
.value(env.getLabel(
"@bazel_tools//tools/test/LcovMerger/java/com/google/devtools/lcovmerger:Main"
)))
.add(
attr("$jacocorunner", LABEL)
.value(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,14 @@ public void setupMockClient(MockToolsConfig config) throws IOException {
"filegroup(name = 'runtime', srcs = ['test-setup.sh'])",
"filegroup(name = 'test_setup', srcs = ['test-setup.sh'])",
"filegroup(name = 'collect_coverage', srcs = ['collect_coverage.sh'])",
"filegroup(name='coverage_support', srcs=['collect_coverage.sh','LcovMerger'])",
"filegroup(name='coverage_support', srcs=['collect_coverage.sh'])",
"filegroup(name = 'coverage_report_generator', srcs = ['coverage_report_generator.sh'])");

config.create(
"/bazel_tools_workspace/tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BUILD",
"filegroup(name='srcs', srcs = glob(['**']))",
"filegroup(name='Main', srcs = ['Main.java'])");

config.create(
"/bazel_tools_workspace/tools/python/BUILD",
"package(default_visibility=['//visibility:public'])",
Expand Down
16 changes: 10 additions & 6 deletions src/test/shell/bazel/bazel_coverage_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -206,19 +206,21 @@ EOF
cat <<EOF > result.dat
SF:com/example/Collatz.java
FN:3,com/example/Collatz::<init> ()V
FNDA:0,com/example/Collatz::<init> ()V
FN:6,com/example/Collatz::getCollatzFinal (I)I
FNDA:0,com/example/Collatz::<init> ()V
FNDA:1,com/example/Collatz::getCollatzFinal (I)I
FNF:0
FNH:0
BA:6,2
BA:6,2
BA:9,2
BA:9,2
DA:3,0
DA:6,3
DA:7,2
DA:9,4
DA:10,5
DA:12,7
LH:0
LF:0
end_of_record
EOF

Expand Down Expand Up @@ -297,19 +299,21 @@ EOF
cat <<EOF > result.dat
SF:src/main/com/example/Collatz.java
FN:3,com/example/Collatz::<init> ()V
FNDA:0,com/example/Collatz::<init> ()V
FN:6,com/example/Collatz::getCollatzFinal (I)I
FNDA:0,com/example/Collatz::<init> ()V
FNDA:1,com/example/Collatz::getCollatzFinal (I)I
FNF:0
FNH:0
BA:6,2
BA:6,2
BA:9,2
BA:9,2
DA:3,0
DA:6,3
DA:7,2
DA:9,4
DA:10,5
DA:12,7
LH:0
LF:0
end_of_record
EOF

Expand Down
4 changes: 4 additions & 0 deletions tools/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ filegroup(
"//tools/osx:srcs",
"//tools/osx/crosstool:srcs",
"//tools/test:srcs",
"//tools/test/LcovMerger/java/com/google/devtools/lcovmerger:srcs",
"//tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger:srcs",
"//tools/python:srcs",
"//tools/runfiles:srcs",
"//tools/sh:srcs",
Expand Down Expand Up @@ -65,10 +67,12 @@ filegroup(
"//tools/python:embedded_tools",
"//tools/runfiles:embedded_tools",
"//tools/test:srcs",
"//tools/test/LcovMerger/java/com/google/devtools/lcovmerger:srcs",
"//tools/osx/crosstool:srcs",
"//tools/osx:srcs",
"//tools/sh:embedded_tools",
"//tools/whitelists:srcs",
"//tools/zip:srcs",
"//third_party:srcs",
],
)
24 changes: 0 additions & 24 deletions tools/test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,6 @@ filegroup(
srcs = ["collect_coverage.sh"],
)

java_binary(
name = "LcovMerger",
srcs = glob(["LcovMerger/java/**/*.java"]),
main_class = "com.google.devtools.lcovmerger.Main",
)

java_library(
name = "LcovMergerTestUtils",
srcs = glob(["LcovMerger/java/**/*.java"]),
)

java_test(
name = "LcovMergerTest",
srcs = glob(["LcovMerger/javatests/**/*.java"]),
deps = [
":LcovMergerTestUtils",
"//src/main/java/com/google/devtools/build/lib/vfs",
"//src/test/java/com/google/devtools/build/lib:foundations_testutil",
"//src/test/java/com/google/devtools/build/lib:testutil",
"//third_party:junit4",
"//third_party:truth",
],
)

filegroup(
name = "coverage_support",
srcs = ["collect_coverage.sh"],
Expand Down
106 changes: 106 additions & 0 deletions tools/test/LcovMerger/java/com/google/devtools/lcovmerger/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package(
default_visibility = [
"//tools/test/LcovMerger/javatests/com/google/devtools/lcovmerger:__pkg__",
],
)

licenses(["notice"]) # Apache 2.0

java_library(
name = "BranchCoverage",
srcs = ["BranchCoverage.java"],
deps = [
"//third_party:auto_value",
],
)

java_library(
name = "LineCoverage",
srcs = ["LineCoverage.java"],
deps = [
"//third_party:auto_value",
"//third_party:jsr305",
],
)

java_library(
name = "SourceFileCoverage",
srcs = ["SourceFileCoverage.java"],
deps = [
":BranchCoverage",
":LineCoverage",
"//third_party:auto_value",
"//third_party:guava",
"//third_party:jsr305",
],
)

java_library(
name = "LcovPrinter",
srcs = ["LcovPrinter.java"],
deps = [
":BranchCoverage",
":Coverage",
":LcovConstants",
":LineCoverage",
":SourceFileCoverage",
"//third_party:guava",
],
)

java_library(
name = "LcovConstants",
srcs = ["LcovConstants.java"],
)

java_library(
name = "LcovParser",
srcs = ["LcovParser.java"],
deps = [
":BranchCoverage",
":LcovConstants",
":LineCoverage",
":SourceFileCoverage",
],
)

java_library(
name = "Coverage",
srcs = ["Coverage.java"],
deps = [":SourceFileCoverage"],
)

java_library(
name = "MainLibrary",
srcs = ["Main.java"],
deps = [
":Coverage",
":LcovConstants",
":LcovParser",
":LcovPrinter",
":SourceFileCoverage",
"//third_party:guava",
],
)

java_binary(
name = "Main",
srcs = ["Main.java"],
main_class = "com.google.devtools.lcovmerger.Main",
visibility = ["//visibility:public"],
deps = [
":Coverage",
":LcovConstants",
":LcovParser",
":LcovPrinter",
":MainLibrary",
":SourceFileCoverage",
"//third_party:guava",
],
)

filegroup(
name = "srcs",
srcs = glob(["**"]),
visibility = ["//visibility:public"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2018 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.devtools.lcovmerger;

import com.google.auto.value.AutoValue;

/**
* Stores branch coverage information.
*/
@AutoValue
abstract class BranchCoverage {

static BranchCoverage create(
int lineNumber, int blockNumber, int branchNumber, boolean wasExecuted, int nrOfExecutions) {
assert (wasExecuted && nrOfExecutions > 0) || (!wasExecuted && nrOfExecutions == 0);
return new AutoValue_BranchCoverage(
lineNumber, blockNumber, branchNumber, wasExecuted, nrOfExecutions);
}

/**
* Merges two given instances of {@link BranchCoverage}.
*
* Calling {@code lineNumber()}, {@code blockNumber()} and {@code branchNumber()} must return the
* same values for {@code first} and {@code second}.
*/
static BranchCoverage merge(BranchCoverage first, BranchCoverage second) {
assert first.lineNumber() == second.lineNumber();
assert first.blockNumber() == second.blockNumber();
assert first.branchNumber() == second.branchNumber();

return create(
first.lineNumber(),
first.blockNumber(),
first.branchNumber(),
first.wasExecuted() || second.wasExecuted(),
first.nrOfExecutions() + second.nrOfExecutions());
}

abstract int lineNumber();
// The two numbers below should be -1 for non-gcc emitted coverage (e.g. Java).
abstract int blockNumber(); // internal gcc internal ID for the branch
abstract int branchNumber(); // internal gcc internal ID for the branch
abstract boolean wasExecuted();
abstract int nrOfExecutions();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2018 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.devtools.lcovmerger;

import java.util.Collection;
import java.util.TreeMap;

class Coverage {
private final TreeMap<String, SourceFileCoverage> sourceFiles;

Coverage() {
sourceFiles = new TreeMap<>();
}

void add(SourceFileCoverage input) {
String sourceFilename = input.sourceFileName();
if (sourceFiles.containsKey(sourceFilename)) {
SourceFileCoverage old = sourceFiles.get(sourceFilename);
sourceFiles.put(sourceFilename, SourceFileCoverage.merge(old, input));
} else {
sourceFiles.put(sourceFilename, input);
}
}

Collection<SourceFileCoverage> getAllSourceFiles() {
return sourceFiles.values();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2018 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.devtools.lcovmerger;

/**
* Stores markers used by the lcov tracefile. See
* <a href="http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php"> lcov documentation</a>
*/
class LcovConstants {
static final String SF_MARKER = "SF:";
static final String FN_MARKER = "FN:";
static final String FNDA_MARKER = "FNDA:";
static final String FNF_MARKER = "FNF:";
static final String FNH_MARKER = "FNH:";
static final String BRDA_MARKER = "BRDA:";
static final String BA_MARKER = "BA:";
static final String BRF_MARKER = "BRF:";
static final String BRH_MARKER = "BRH:";
static final String DA_MARKER = "DA:";
static final String LH_MARKER = "LH:";
static final String LF_MARKER = "LF:";
static final String END_OF_RECORD_MARKER = "end_of_record";
static final String LCOV_DELIMITER = ",";
static final String TAKEN = "-";
static final String TRACEFILE_EXTENSION = ".dat";
}
Loading

0 comments on commit b00c8e7

Please sign in to comment.