blob: 72ac862a3cf897cbb57ebb9d218bffbd4c4c7200 [file] [log] [blame]
// Copyright 2020 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.coverageoutputgenerator;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
/**
* A {@link Parser} for gcov intermediate json format introduced in GCC 9.1. See the flag {@code
* --intermediate-format} in <a
* href="https://gcc.gnu.org/onlinedocs/gcc-9.3.0/gcc/Invoking-Gcov.html">gcov documentation</a>.
*/
public class GcovJsonParser {
private static final Logger logger = Logger.getLogger(GcovJsonParser.class.getName());
private final InputStream inputStream;
private GcovJsonParser(InputStream inputStream) {
this.inputStream = inputStream;
}
public static List<SourceFileCoverage> parse(InputStream inputStream) throws IOException {
return new GcovJsonParser(inputStream).parse();
}
private List<SourceFileCoverage> parse() throws IOException {
ArrayList<SourceFileCoverage> allSourceFiles = new ArrayList<>();
try (InputStream gzipStream = new GZIPInputStream(inputStream)) {
ByteArrayOutputStream contents = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = gzipStream.read(buffer)) != -1) {
contents.write(buffer, 0, length);
}
Gson gson = new Gson();
GcovJsonFormat document = gson.fromJson(contents.toString(), GcovJsonFormat.class);
if (!document.format_version.equals("1")) {
logger.log(
Level.WARNING,
"Expect GCov JSON format version 1, got format version " + document.format_version);
}
for (GcovJsonFile file : document.files) {
SourceFileCoverage currentFileCoverage = new SourceFileCoverage(file.file);
for (GcovJsonFunction function : file.functions) {
currentFileCoverage.addLineNumber(function.name, function.start_line);
currentFileCoverage.addFunctionExecution(function.name, function.execution_count);
}
for (GcovJsonLine line : file.lines) {
currentFileCoverage.addLine(
line.line_number, LineCoverage.create(line.line_number, line.count, null));
for (GcovJsonBranch branch : line.branches) {
currentFileCoverage.addBranch(
line.line_number, BranchCoverage.create(line.line_number, branch.count));
}
}
allSourceFiles.add(currentFileCoverage);
}
}
return allSourceFiles;
}
// Classes for the Gson data mapper representing the structure of the GCov JSON format
// These do not follow the Java naming styleguide as they need to match the JSON field names
// Documentation can be found in GCov's manpage, of which the source is available at
// https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/doc/gcov.texi;h=dcdd7831ff063483d43e5347af0b67083c85ecc4;hb=4212a6a3e44f870412d9025eeb323fd4f50a61da#l184
static class GcovJsonFormat {
String gcc_version;
GcovJsonFile[] files;
String format_version;
String current_working_directory;
String data_file;
}
static class GcovJsonFile {
String file;
GcovJsonFunction[] functions;
GcovJsonLine[] lines;
}
static class GcovJsonFunction {
int blocks;
int end_column;
int start_line;
String name;
int blocks_executed;
int execution_count;
String demangled_name;
int start_column;
int end_line;
}
static class GcovJsonLine {
GcovJsonBranch[] branches;
int count;
int line_number;
boolean unexecuted_block;
String function_name;
}
static class GcovJsonBranch {
boolean fallthrough;
int count;
@SerializedName("throw")
boolean _throw;
}
}