ANDROID: Move android scripts to kernel/tests/
kernel/tests/ is shared with platform branches. The scripts can be combined with platform tests. Bug: 355559072 Test: None Change-Id: I1bf03653ccbc11480f944546c1945bb0ea768ff0 Signed-off-by: Hsin-Yi Chen <hsinyichen@google.com>
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
bettyzhou@google.com
|
||||
edliaw@google.com
|
||||
hwj@google.com
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# acloudb .sh is a handy tool dedicated to kernel users to create remote AVDs without having the
|
||||
# whole AOSP source tree.
|
||||
|
||||
# Constants
|
||||
DEFAULT_ASUITE_HOME="prebuilts/asuite"
|
||||
DEFAULT_ACLOUD_HOME="$DEFAULT_ASUITE_HOME/acloud/linux-x86"
|
||||
ACLOUD_BIN="$DEFAULT_ACLOUD_HOME/acloud"
|
||||
OPT_SKIP_PRERUNCHECK='--skip-pre-run-check'
|
||||
OPT_DEFAULT_BRANCH=" --branch aosp-main"
|
||||
# Color constants
|
||||
BOLD="$(tput bold)"
|
||||
END="$(tput sgr0)"
|
||||
GREEN="$(tput setaf 2)"
|
||||
RED="$(tput setaf 198)"
|
||||
|
||||
function adb_checker() {
|
||||
[[ "$(uname)" != "Linux" ]] && return
|
||||
if ! which adb &> /dev/null; then
|
||||
echo -e "\n${RED}Adb not found!${END}"
|
||||
fi
|
||||
}
|
||||
|
||||
function main() {
|
||||
adb_checker
|
||||
EXTRA_OPTIONS=()
|
||||
if [[ "$1" == "create" ]]; then
|
||||
EXTRA_OPTIONS+=$OPT_SKIP_PRERUNCHECK
|
||||
# Add in branch if not specified
|
||||
ADD_BRANCH=true
|
||||
for i in "$@"; do
|
||||
[[ $i == "--branch" ]] && ADD_BRANCH=false
|
||||
done
|
||||
if $ADD_BRANCH; then
|
||||
EXTRA_OPTIONS+=$OPT_DEFAULT_BRANCH
|
||||
fi
|
||||
fi
|
||||
eval "$ACLOUD_BIN" "$@" "${EXTRA_OPTIONS[@]}"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -1,554 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright (C) 2024 The Android Open Source Project
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""This utility generates a single lcov tracefile from a gcov tar file."""
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import fnmatch
|
||||
import glob
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import shutil
|
||||
import sys
|
||||
import tarfile
|
||||
|
||||
|
||||
LCOV = "lcov"
|
||||
|
||||
# Relative to the root of the source tree..
|
||||
OUTPUT_COV_DIR = os.path.join("out", "coverage")
|
||||
|
||||
PREBUILT_LLVM_COV_PATH = os.path.join(
|
||||
"prebuilts", "clang", "host", "linux-x86", "llvm-binutils-stable",
|
||||
"llvm-cov"
|
||||
)
|
||||
|
||||
EXCLUDED_FILES = [
|
||||
"*/security/selinux/av_permissions.h",
|
||||
"*/security/selinux/flask.h",
|
||||
]
|
||||
|
||||
|
||||
def create_llvm_gcov_sh(
|
||||
llvm_cov_filename: str,
|
||||
llvm_gcov_sh_filename: str,
|
||||
) -> None:
|
||||
"""Create a shell script that is compatible with gcov.
|
||||
|
||||
Args:
|
||||
llvm_cov_filename: The absolute path to llvm-cov.
|
||||
llvm_gcov_sh_filename: The path to the script to be created.
|
||||
"""
|
||||
file_path = pathlib.Path(llvm_gcov_sh_filename)
|
||||
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
file_path.write_text(f'#!/bin/bash\nexec {llvm_cov_filename} gcov "$@"')
|
||||
os.chmod(llvm_gcov_sh_filename, 0o755)
|
||||
|
||||
|
||||
def generate_lcov_tracefile(
|
||||
gcov_dir: str,
|
||||
kernel_source: str,
|
||||
gcov_filename: str,
|
||||
tracefile_filename: str,
|
||||
included_files: [],
|
||||
) -> None:
|
||||
"""Call lcov to create tracefile based on gcov data files.
|
||||
|
||||
Args:
|
||||
gcov_dir: Directory that contains the extracted gcov data files as retreived
|
||||
from debugfs.
|
||||
kernel_source: Directory containing the kernel source same as what was used
|
||||
to build system under test.
|
||||
gcov_filename: The absolute path to gcov or a compatible script.
|
||||
tracefile_filename: The name of tracefile to create.
|
||||
included_files: List of source file pattern to include in tracefile. Can be
|
||||
empty in which case include allo source.
|
||||
"""
|
||||
exclude_args = " ".join([f'--exclude "{f}"' for f in EXCLUDED_FILES])
|
||||
include_args = (
|
||||
" ".join([f'--include "{f[0]}"' for f in included_files])
|
||||
if included_files is not None
|
||||
else ""
|
||||
)
|
||||
|
||||
logging.info("Running lcov on %s", gcov_dir)
|
||||
lcov_cmd = (
|
||||
f"{LCOV} -q "
|
||||
"--ignore-errors=source "
|
||||
"--rc branch_coverage=1 "
|
||||
f"-b {kernel_source} "
|
||||
f"-d {gcov_dir} "
|
||||
f"--gcov-tool {gcov_filename} "
|
||||
f"{exclude_args} "
|
||||
f"{include_args} "
|
||||
"--ignore-errors gcov,gcov,unused,unused "
|
||||
"--capture "
|
||||
f"-o {tracefile_filename} "
|
||||
)
|
||||
os.system(lcov_cmd)
|
||||
|
||||
|
||||
def update_symlink_from_mapping(filepath: str, prefix_mappings: {}) -> bool:
|
||||
"""Update symbolic link based on prefix mappings.
|
||||
|
||||
It will attempt to update the given symbolic link based on the prefix
|
||||
mappings. For every "from" prefix that matches replace with the new "to"
|
||||
value. If the resulting path doesn't exist, try the next.
|
||||
|
||||
Args:
|
||||
filepath: Path of symbolic link to update.
|
||||
prefix_mappings: A multimap where the key is the "from" prefix to match, and
|
||||
the value is an array of "to" values to attempt to replace with.
|
||||
|
||||
Returns:
|
||||
True or false depending on the whether symbolic link was successfully
|
||||
updated to a new path that exists.
|
||||
"""
|
||||
|
||||
link_target = os.readlink(filepath)
|
||||
for old_prefix, new_prefix_list in prefix_mappings.items():
|
||||
for new_prefix in new_prefix_list:
|
||||
if link_target.startswith(old_prefix):
|
||||
new_target = os.path.abspath(
|
||||
link_target.replace(old_prefix, new_prefix)
|
||||
)
|
||||
if not os.path.exists(new_target):
|
||||
continue
|
||||
os.unlink(filepath) # Remove the old symbolic link
|
||||
os.symlink(new_target, filepath) # Create the updated link
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def correct_symlinks_in_directory(directory: str, prefix_mappings: {}) -> None:
|
||||
"""Recursively traverses a directory, updating symbolic links.
|
||||
|
||||
Replaces 'old_prefix' in the link destination with 'new_prefix'.
|
||||
|
||||
Args:
|
||||
directory: The root directory to traverse.
|
||||
prefix_mappings: Dictionary where the keys are the old prefixes and the
|
||||
values are the new prefixes
|
||||
"""
|
||||
|
||||
logging.info("Fixing up symbolic links in %s", directory)
|
||||
|
||||
for root, _, files in os.walk(directory):
|
||||
for filename in files:
|
||||
filepath = os.path.join(root, filename)
|
||||
if os.path.islink(filepath):
|
||||
if not update_symlink_from_mapping(filepath, prefix_mappings):
|
||||
logging.error(
|
||||
"Unable to update link at %s with any prefix mappings: %s",
|
||||
filepath,
|
||||
prefix_mappings,
|
||||
)
|
||||
sys.exit(-1)
|
||||
|
||||
|
||||
def find_most_recent_tarfile(path: str, pattern: str = "*.tar.gz") -> str:
|
||||
"""Attempts to find a valid tar file given the location.
|
||||
|
||||
If location is a directory finds the most recent tarfile or if location is a
|
||||
a valid tar file returns, if neither of these return None.
|
||||
|
||||
Args:
|
||||
path (str): The path to either a tarfile or a directory.
|
||||
pattern (str, optional): Glob pattern for matching tarfiles. Defaults to
|
||||
"*.tar.gz".
|
||||
|
||||
Returns:
|
||||
str: The path to the most recent tarfile found, or the original path
|
||||
if it was a valid tarfile. None if no matching tarfiles are found.
|
||||
"""
|
||||
|
||||
if os.path.isfile(path):
|
||||
if tarfile.is_tarfile(path):
|
||||
return path # Path is a valid tarfile
|
||||
return None # Path is a file but not a tar file
|
||||
|
||||
if os.path.isdir(path):
|
||||
results = []
|
||||
for root, _, files in os.walk(path):
|
||||
for file in files:
|
||||
if fnmatch.fnmatch(file, pattern):
|
||||
full_path = os.path.join(root, file)
|
||||
results.append((full_path, os.path.getmtime(full_path)))
|
||||
|
||||
if results:
|
||||
return max(results, key=lambda item: item[1])[
|
||||
0
|
||||
] # Return path of the most recent one
|
||||
else:
|
||||
return None # No tarfiles found in the directory
|
||||
|
||||
return None # Path is neither a tarfile nor a directory
|
||||
|
||||
|
||||
def make_absolute(path: str, base_dir: str) -> str:
|
||||
if os.path.isabs(path):
|
||||
return path
|
||||
|
||||
return os.path.join(base_dir, path)
|
||||
|
||||
|
||||
def append_slash(path: str) -> str:
|
||||
if path is not None and path[-1] != "/":
|
||||
path += "/"
|
||||
return path
|
||||
|
||||
|
||||
def update_multimap_from_json(
|
||||
json_file: str, base_dir: str, result_multimap: collections.defaultdict
|
||||
) -> None:
|
||||
"""Reads 'to' and 'from' fields from a JSON file and updates a multimap.
|
||||
|
||||
'from' refers to a bazel sandbox directory.
|
||||
'to' refers to the output directory of gcno files.
|
||||
The multimap is implemented as a dictionary of lists allowing multiple 'to'
|
||||
values for each 'from' key.
|
||||
|
||||
Sample input:
|
||||
[
|
||||
{
|
||||
"from": "/sandbox/1/execroot/_main/out/android-mainline/common",
|
||||
"to": "bazel-out/k8-fastbuild/bin/common/kernel_x86_64/kernel_x86_64_gcno"
|
||||
},
|
||||
{
|
||||
"from": "/sandbox/2/execroot/_main/out/android-mainline/common",
|
||||
"to": "bazel-out/k8-fastbuild/bin/common-modules/virtual-device/virtual_device_x86_64/virtual_device_x86_64_gcno"
|
||||
}
|
||||
]
|
||||
|
||||
Args:
|
||||
json_file: The path to the JSON file.
|
||||
base_dir: Used if either of the 'to' or 'from' paths are relative to make
|
||||
them absolute by prepending this base_dir value.
|
||||
result_multimap: A multimap that is updated with every 'to' and 'from'
|
||||
found.
|
||||
|
||||
Returns:
|
||||
The updated dictionary.
|
||||
"""
|
||||
with open(json_file, "r") as file:
|
||||
data = json.load(file)
|
||||
|
||||
for item in data:
|
||||
to_value = append_slash(item.get("to"))
|
||||
from_value = append_slash(item.get("from"))
|
||||
if to_value and from_value:
|
||||
to_value = make_absolute(to_value, base_dir)
|
||||
from_value = make_absolute(from_value, base_dir)
|
||||
result_multimap[from_value].append(to_value)
|
||||
|
||||
|
||||
def read_gcno_mapping_files(
|
||||
search_dir_pattern: str,
|
||||
base_dir: str,
|
||||
result_multimap: collections.defaultdict
|
||||
) -> None:
|
||||
"""Search a directory for gcno_mapping."""
|
||||
found = False
|
||||
pattern = os.path.join(search_dir_pattern, "gcno_mapping.*.json")
|
||||
for filepath in glob.iglob(pattern, recursive=False):
|
||||
found = True
|
||||
logging.info("Reading %s", filepath)
|
||||
update_multimap_from_json(filepath, base_dir, result_multimap)
|
||||
|
||||
if not found:
|
||||
logging.error("No gcno_mapping in %s", search_dir_pattern)
|
||||
|
||||
|
||||
def read_gcno_dir(
|
||||
gcno_dir: str, result_multimap: collections.defaultdict
|
||||
) -> None:
|
||||
"""Read a directory containing gcno_mapping and gcno files."""
|
||||
multimap = collections.defaultdict(list)
|
||||
read_gcno_mapping_files(gcno_dir, gcno_dir, multimap)
|
||||
|
||||
to_value = append_slash(os.path.abspath(gcno_dir))
|
||||
for from_value in multimap:
|
||||
result_multimap[from_value].append(to_value)
|
||||
|
||||
|
||||
def get_testname_from_filename(file_path: str) -> str:
|
||||
filename = os.path.basename(file_path)
|
||||
if "_kernel_coverage" in filename:
|
||||
tmp = filename[: filename.find("_kernel_coverage")]
|
||||
testname = tmp[: tmp.rfind("_")]
|
||||
else:
|
||||
testname = filename[: filename.rfind("_")]
|
||||
return testname
|
||||
|
||||
|
||||
def unpack_gcov_tar(file_path: str, output_dir: str) -> str:
|
||||
"""Unpack the tar file into the specified directory.
|
||||
|
||||
Args:
|
||||
file_path: The path of the tar file to be unpacked.
|
||||
output_dir: The root directory where the unpacked folder will reside.
|
||||
|
||||
Returns:
|
||||
The path of extracted data.
|
||||
"""
|
||||
|
||||
testname = get_testname_from_filename(file_path)
|
||||
logging.info(
|
||||
"Unpacking %s for test %s...", os.path.basename(file_path), testname
|
||||
)
|
||||
|
||||
test_dest_dir = os.path.join(output_dir, testname)
|
||||
if os.path.exists(test_dest_dir):
|
||||
shutil.rmtree(test_dest_dir)
|
||||
os.makedirs(test_dest_dir)
|
||||
shutil.unpack_archive(file_path, test_dest_dir, "tar")
|
||||
return test_dest_dir
|
||||
|
||||
|
||||
def get_parent_path(path: str, levels_up: int) -> str:
|
||||
"""Goes up a specified number of levels from a given path.
|
||||
|
||||
Args:
|
||||
path: The path to find desired ancestor.
|
||||
levels_up: The number of levels up to go.
|
||||
|
||||
Returns:
|
||||
The desired ancestor of the given path.
|
||||
"""
|
||||
p = pathlib.Path(path)
|
||||
for _ in range(levels_up):
|
||||
p = p.parent
|
||||
return str(p)
|
||||
|
||||
|
||||
def get_kernel_repo_dir() -> str:
|
||||
this_script_full_path = os.path.abspath(__file__)
|
||||
|
||||
# Assume this script is placed this many places from
|
||||
# the base kernel repo directory, e.g.:
|
||||
# kernel_repo/common/tools/testing/android/bin/<this_script>
|
||||
return get_parent_path(this_script_full_path, 6)
|
||||
|
||||
|
||||
class Config:
|
||||
"""The input and output paths of this script."""
|
||||
|
||||
def __init__(self, repo_dir: str, llvm_cov_path: str, tmp_dir: str):
|
||||
"""Each argument can be empty."""
|
||||
self._repo_dir = os.path.abspath(repo_dir) if repo_dir else None
|
||||
self._llvm_cov_path = (
|
||||
os.path.abspath(llvm_cov_path) if llvm_cov_path else None
|
||||
)
|
||||
self._tmp_dir = os.path.abspath(tmp_dir) if tmp_dir else None
|
||||
self._repo_out_dir = None
|
||||
|
||||
@property
|
||||
def repo_dir(self) -> str:
|
||||
if not self._repo_dir:
|
||||
self._repo_dir = get_kernel_repo_dir()
|
||||
return self._repo_dir
|
||||
|
||||
def _get_repo_path(self, rel_path: str) -> str:
|
||||
repo_path = os.path.join(self.repo_dir, rel_path)
|
||||
if not os.path.exists(repo_path):
|
||||
logging.error(
|
||||
"%s does not exist. If this script is not in the source directory,"
|
||||
" specify --repo-dir. If you do not have full kernel source,"
|
||||
" specify --llvm-cov, --gcno-dir, and --tmp-dir.",
|
||||
repo_path,
|
||||
)
|
||||
sys.exit(-1)
|
||||
return repo_path
|
||||
|
||||
@property
|
||||
def llvm_cov_path(self) -> str:
|
||||
if not self._llvm_cov_path:
|
||||
self._llvm_cov_path = self._get_repo_path(PREBUILT_LLVM_COV_PATH)
|
||||
return self._llvm_cov_path
|
||||
|
||||
@property
|
||||
def repo_out_dir(self) -> str:
|
||||
if not self._repo_out_dir:
|
||||
self._repo_out_dir = self._get_repo_path("out")
|
||||
return self._repo_out_dir
|
||||
|
||||
@property
|
||||
def tmp_dir(self) -> str:
|
||||
if not self._tmp_dir:
|
||||
# Temporary directory does not have to exist.
|
||||
self._tmp_dir = os.path.join(self.repo_dir, OUTPUT_COV_DIR)
|
||||
return self._tmp_dir
|
||||
|
||||
@property
|
||||
def llvm_gcov_sh_path(self) -> str:
|
||||
return os.path.join(self.tmp_dir, "tmp", "llvm-gcov.sh")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
arg_parser = argparse.ArgumentParser(
|
||||
description="Generate lcov tracefiles from gcov file dumps"
|
||||
)
|
||||
|
||||
arg_parser.add_argument(
|
||||
"-t",
|
||||
dest="tar_location",
|
||||
required=True,
|
||||
help=(
|
||||
"Either a path to a gcov tar file or a directory that contains gcov"
|
||||
" tar file(s). The gcov tar file is expected to be created from"
|
||||
" Tradefed. If a directory is used, will search the entire directory"
|
||||
" for files matching *_kernel_coverage*.tar.gz and select the most"
|
||||
" recent one."
|
||||
),
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"-o",
|
||||
dest="out_file",
|
||||
required=False,
|
||||
help="Name of output tracefile generated. Default: cov.info",
|
||||
default="cov.info",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--include",
|
||||
action="append",
|
||||
nargs=1,
|
||||
required=False,
|
||||
help=(
|
||||
"File pattern of source file(s) to include in generated tracefile."
|
||||
" Multiple patterns can be specified by using multiple --include"
|
||||
" command line switches. If no includes are specified all source is"
|
||||
" included."
|
||||
),
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--repo-dir",
|
||||
required=False,
|
||||
help="Root directory of kernel source"
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--dist-dir",
|
||||
dest="dist_dirs",
|
||||
action="append",
|
||||
default=[],
|
||||
required=False,
|
||||
help="Dist directory containing gcno mapping files"
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--gcno-dir",
|
||||
dest="gcno_dirs",
|
||||
action="append",
|
||||
default=[],
|
||||
required=False,
|
||||
help="Path to an extracted .gcno.tar.gz"
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--llvm-cov",
|
||||
required=False,
|
||||
help=(
|
||||
"Path to llvm-cov. Default: " +
|
||||
os.path.join("<repo dir>", PREBUILT_LLVM_COV_PATH)
|
||||
)
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--tmp-dir",
|
||||
required=False,
|
||||
help=(
|
||||
"Path to the directory where the temporary files are created."
|
||||
" Default: " + os.path.join("<repo dir>", OUTPUT_COV_DIR)
|
||||
)
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Enable verbose logging",
|
||||
)
|
||||
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
else:
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
||||
if shutil.which(LCOV) is None:
|
||||
logging.error(
|
||||
"%s is not found and is required for this script. Please install from:",
|
||||
LCOV,
|
||||
)
|
||||
logging.critical(" https://github.com/linux-test-project/lcov")
|
||||
sys.exit(-1)
|
||||
|
||||
if args.repo_dir and not os.path.isdir(args.repo_dir):
|
||||
logging.error("%s is not a directory.", args.repo_dir)
|
||||
sys.exit(-1)
|
||||
|
||||
if args.llvm_cov and not os.path.isfile(args.llvm_cov):
|
||||
logging.error("%s is not a file.", args.llvm_cov)
|
||||
sys.exit(-1)
|
||||
|
||||
for gcno_dir in args.gcno_dirs + args.dist_dirs:
|
||||
if not os.path.isdir(gcno_dir):
|
||||
logging.error("%s is not a directory.", gcno_dir)
|
||||
sys.exit(-1)
|
||||
|
||||
config = Config(args.repo_dir, args.llvm_cov, args.tmp_dir)
|
||||
|
||||
gcno_mappings = collections.defaultdict(list)
|
||||
if not args.gcno_dirs and not args.dist_dirs:
|
||||
dist_dir_pattern = os.path.join(config.repo_out_dir, "**", "dist")
|
||||
read_gcno_mapping_files(dist_dir_pattern, config.repo_dir, gcno_mappings)
|
||||
|
||||
for dist_dir in args.dist_dirs:
|
||||
read_gcno_mapping_files(dist_dir, config.repo_dir, gcno_mappings)
|
||||
|
||||
for gcno_dir in args.gcno_dirs:
|
||||
read_gcno_dir(gcno_dir, gcno_mappings)
|
||||
|
||||
if not gcno_mappings:
|
||||
# read_gcno_mapping_files prints the error messages
|
||||
sys.exit(-1)
|
||||
|
||||
tar_file = find_most_recent_tarfile(
|
||||
args.tar_location, pattern="*kernel_coverage_*.tar.gz"
|
||||
)
|
||||
if tar_file is None:
|
||||
logging.error("Unable to find a gcov tar under %s", args.tar_location)
|
||||
sys.exit(-1)
|
||||
|
||||
gcov_dir = unpack_gcov_tar(tar_file, config.tmp_dir)
|
||||
correct_symlinks_in_directory(gcov_dir, gcno_mappings)
|
||||
|
||||
create_llvm_gcov_sh(
|
||||
config.llvm_cov_path,
|
||||
config.llvm_gcov_sh_path,
|
||||
)
|
||||
|
||||
generate_lcov_tracefile(
|
||||
gcov_dir,
|
||||
config.repo_dir,
|
||||
config.llvm_gcov_sh_path,
|
||||
args.out_file,
|
||||
args.include,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,227 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
BAZEL=tools/bazel
|
||||
BIN_DIR=common/tools/testing/android/bin
|
||||
ACLOUD=$BIN_DIR/acloudb.sh
|
||||
TRADEFED=prebuilts/tradefed/filegroups/tradefed/tradefed.sh
|
||||
DEFAULT_DIST_DIR=out/virtual_device_x86_64/dist
|
||||
TESTSDIR=/tmp/kselftests
|
||||
LOG_DIR=$PWD/out/test_logs/$(date +%Y%m%d_%H%M%S)
|
||||
JDK_PATH=prebuilts/jdk/jdk11/linux-x86
|
||||
|
||||
print_help() {
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "This script runs Selftests on an Android device."
|
||||
echo "Please run the script with full path common/tools/testing/android/bin/."
|
||||
echo "Building kernel and launching virtual device are enabled by default;"
|
||||
echo "use options to skip the workflow."
|
||||
echo ""
|
||||
echo "Available options:"
|
||||
echo " --skip-kernel-build Skip the kernel building step"
|
||||
echo " --skip-cvd-launch Skip the CVD launch step"
|
||||
echo " --skip-cvd-kill Do not kill CVD launched by running this script"
|
||||
echo " -d, --dist-dir=DIR The kernel dist dir (default is $DEFAULT_DIST_DIR)"
|
||||
echo " -s, --serial=SERIAL The device serial number."
|
||||
echo " If serial is specified, virtual device launch will be skipped"
|
||||
echo " -t, --test=TEST_NAME The test target name. Can be repeated"
|
||||
echo " If test is not specified, all kselftests will be run"
|
||||
echo " --gcov Collect coverage data from the test result"
|
||||
echo " -h, --help Display this help message and exit"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo "$0"
|
||||
echo "$0 -t kselftest_size_test_get_size -t kselftest_binderfs_binderfs_test"
|
||||
echo "$0 -s 127.0.0.1:45549"
|
||||
echo ""
|
||||
exit 0
|
||||
}
|
||||
|
||||
BUILD_KERNEL=true
|
||||
LAUNCH_CVD=true
|
||||
KILL_CVD=true
|
||||
DIST_DIR=$DEFAULT_DIST_DIR
|
||||
SERIAL_NUMBER=
|
||||
MODULE_NAME="selftests"
|
||||
TEST_FILTERS=
|
||||
SELECTED_TESTS=
|
||||
GCOV=false
|
||||
GCOV_DIST_DIR=
|
||||
|
||||
while test $# -gt 0; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
print_help
|
||||
;;
|
||||
--skip-kernel-build)
|
||||
BUILD_KERNEL=false
|
||||
shift
|
||||
;;
|
||||
--skip-cvd-launch)
|
||||
LAUNCH_CVD=false
|
||||
shift
|
||||
;;
|
||||
--skip-cvd-kill)
|
||||
KILL_CVD=false
|
||||
shift
|
||||
;;
|
||||
-d)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
DIST_DIR=$1
|
||||
GCOV_DIST_DIR=$DIST_DIR
|
||||
else
|
||||
echo "kernel distribution directory is not specified"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--dist-dir*)
|
||||
DIST_DIR=$(echo $1 | sed -e "s/^[^=]*=//g")
|
||||
GCOV_DIST_DIR=$DIST_DIR
|
||||
shift
|
||||
;;
|
||||
-s)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
SERIAL_NUMBER=$1
|
||||
BUILD_KERNEL=false
|
||||
LAUNCH_CVD=false
|
||||
KILL_CVD=false
|
||||
else
|
||||
echo "device serial is not specified"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--serial*)
|
||||
BUILD_KERNEL=false
|
||||
LAUNCH_CVD=false
|
||||
KILL_CVD=false
|
||||
SERIAL_NUMBER=$(echo $1 | sed -e "s/^[^=]*=//g")
|
||||
shift
|
||||
;;
|
||||
-t)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
TEST_NAME=$1
|
||||
SELECTED_TESTS+="$TEST_NAME "
|
||||
TEST_FILTERS+="--include-filter '$MODULE_NAME $TEST_NAME' "
|
||||
else
|
||||
echo "test name is not specified"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--test*)
|
||||
TEST_NAME=$(echo $1 | sed -e "s/^[^=]*=//g")
|
||||
SELECTED_TESTS+="$TEST_NAME "
|
||||
TEST_FILTERS+="--include-filter '$MODULE_NAME $TEST_NAME'"
|
||||
shift
|
||||
;;
|
||||
--gcov)
|
||||
GCOV=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "unknown argument: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if $BUILD_KERNEL; then
|
||||
BUILD_FLAGS=
|
||||
if $GCOV; then
|
||||
BUILD_FLAGS+=" --gcov"
|
||||
fi
|
||||
echo "Building kernel..."
|
||||
# TODO: add support to build kernel for physical device
|
||||
$BAZEL run $BUILD_FLAGS //common-modules/virtual-device:virtual_device_x86_64_dist -- \
|
||||
--destdir=$DIST_DIR
|
||||
exit_code=$?
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
echo "Build kernel succeeded"
|
||||
else
|
||||
echo "Build kernel failed with exit code $exit_code"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if $LAUNCH_CVD; then
|
||||
echo "Launching cvd..."
|
||||
CVD_OUT=$($ACLOUD create --local-kernel-image $DIST_DIR)
|
||||
echo $CVD_OUT
|
||||
INSTANCE_NAME=$(echo "$CVD_OUT" | grep -o "ins-[^\[]*")
|
||||
SERIAL_STRING=$(echo "$CVD_OUT" | grep -oE 'device serial: ([0-9]+\.){3}[0-9]+:[0-9]+')
|
||||
SERIAL_NUMBER=$(echo "$SERIAL_STRING" | sed 's/device serial: //')
|
||||
echo "acloud launched device $SERIAL_NUMBER with instance $INSTANCE_NAME"
|
||||
fi
|
||||
|
||||
if [ -z "$SERIAL_NUMBER" ]; then
|
||||
echo "Device serial is not provided by acloud or by command line flag -s|--serial flag"
|
||||
exit 1
|
||||
else
|
||||
echo "Test with device: $SERIAL_NUMBER"
|
||||
fi
|
||||
|
||||
echo "Get abi from device $SERIAL_NUMBER"
|
||||
ABI=$(adb -s $SERIAL_NUMBER shell getprop ro.product.cpu.abi)
|
||||
echo "Building kselftests according to device $SERIAL_NUMBER ro.product.cpu.abi $ABI ..."
|
||||
case $ABI in
|
||||
arm64*)
|
||||
$BAZEL run //common:kselftest_tests_arm64_install -- --destdir $TESTSDIR
|
||||
;;
|
||||
x86_64*)
|
||||
$BAZEL run //common:kselftest_tests_x86_64_install -- --destdir $TESTSDIR
|
||||
;;
|
||||
*)
|
||||
echo "$ABI not supported"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit_code=$?
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
echo "Build kselftest succeeded"
|
||||
else
|
||||
echo "Build kselftest failed with exit code $exit_code"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$SELECTED_TESTS" ]; then
|
||||
echo "Running all kselftests with device $SERIAL_NUMBER..."
|
||||
TEST_FILTERS="--include-filter $MODULE_NAME"
|
||||
else
|
||||
echo "Running $SELECTED_TESTS with device $SERIAL_NUMBER ..."
|
||||
fi
|
||||
|
||||
tf_cli="JAVA_HOME=$JDK_PATH PATH=$JDK_PATH/bin:$PATH $TRADEFED run commandAndExit \
|
||||
template/local_min --template:map test=suite/test_mapping_suite \
|
||||
$TEST_FILTERS --tests-dir=$TESTSDIR --log-file-path=$LOG_DIR \
|
||||
--primary-abi-only -s $SERIAL_NUMBER"
|
||||
|
||||
if $GCOV; then
|
||||
tf_cli+=" --coverage --coverage-toolchain GCOV_KERNEL --auto-collect GCOV_KERNEL_COVERAGE"
|
||||
fi
|
||||
|
||||
echo "Running tradefed command: $tf_cli"
|
||||
|
||||
eval $tf_cli
|
||||
|
||||
echo "Test finished. Log directory: $LOG_DIR"
|
||||
|
||||
if $LAUNCH_CVD && $KILL_CVD; then
|
||||
echo "Deleting cvd instance $INSTANCE_NAME ..."
|
||||
$ACLOUD delete --instance-names $INSTANCE_NAME
|
||||
fi
|
||||
|
||||
if $GCOV; then
|
||||
CREATE_TRACEFILE_CLI="common/tools/testing/android/bin/create-tracefile.py \
|
||||
-t $LOG_DIR -o $LOG_DIR/cov.info"
|
||||
if [ -n "$GCOV_DIST_DIR" ]; then
|
||||
CREATE_TRACEFILE_CLI+=" --dist-dir $GCOV_DIST_DIR"
|
||||
fi
|
||||
echo "Creating tracefile ..."
|
||||
$CREATE_TRACEFILE_CLI && echo "Created tracefile at $LOG_DIR/cov.info"
|
||||
fi
|
||||
@@ -1,236 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
BAZEL=tools/bazel
|
||||
BIN_DIR=common/tools/testing/android/bin
|
||||
ACLOUD=$BIN_DIR/acloudb.sh
|
||||
TRADEFED=prebuilts/tradefed/filegroups/tradefed/tradefed.sh
|
||||
DEFAULT_DIST_DIR=out/virtual_device_x86_64/dist
|
||||
TESTSDIR=/tmp/kunit
|
||||
LOG_DIR=$PWD/out/test_logs/$(date +%Y%m%d_%H%M%S)
|
||||
JDK_PATH=prebuilts/jdk/jdk11/linux-x86
|
||||
|
||||
print_help() {
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "This script runs KUnit tests on an Android device."
|
||||
echo "Please run the script with full path common/tools/testing/android/bin/."
|
||||
echo "Building kernel and launching virtual device are enabled by default;"
|
||||
echo "use options to skip the workflow."
|
||||
echo ""
|
||||
echo "Available options:"
|
||||
echo " --skip-kernel-build Skip the kernel building step"
|
||||
echo " --skip-cvd-launch Skip the CVD launch step"
|
||||
echo " --skip-cvd-kill Do not kill CVD launched by running this script"
|
||||
echo " -d, --dist-dir=DIR The kernel dist dir (default is $DEFAULT_DIST_DIR)"
|
||||
echo " -s, --serial=SERIAL The device serial number."
|
||||
echo " If serial is specified, virtual device launch will be skipped"
|
||||
echo " -t, --test=TEST_NAME The test target name. Can be repeated"
|
||||
echo " If test is not specified, all tests will be run"
|
||||
echo " --gcov Collect coverage data from the test result"
|
||||
echo " -h, --help Display this help message and exit"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo "$0"
|
||||
echo "$0 -t regmap-kunit.ko -t lib_test.ko"
|
||||
echo "$0 -s 127.0.0.1:45549"
|
||||
echo ""
|
||||
exit 0
|
||||
}
|
||||
|
||||
BUILD_KERNEL=true
|
||||
LAUNCH_CVD=true
|
||||
KILL_CVD=true
|
||||
DIST_DIR=$DEFAULT_DIST_DIR
|
||||
SERIAL_NUMBER=
|
||||
MODULE_NAME="kunit"
|
||||
TEST_FILTERS=
|
||||
SELECTED_TESTS=
|
||||
GCOV=false
|
||||
GCOV_DIST_DIR=
|
||||
|
||||
while test $# -gt 0; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
print_help
|
||||
;;
|
||||
--skip-kernel-build)
|
||||
BUILD_KERNEL=false
|
||||
shift
|
||||
;;
|
||||
--skip-cvd-launch)
|
||||
LAUNCH_CVD=false
|
||||
shift
|
||||
;;
|
||||
--skip-cvd-kill)
|
||||
KILL_CVD=false
|
||||
shift
|
||||
;;
|
||||
-d)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
DIST_DIR=$1
|
||||
GCOV_DIST_DIR=$DIST_DIR
|
||||
else
|
||||
echo "kernel distribution directory is not specified"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--dist-dir*)
|
||||
DIST_DIR=$(echo $1 | sed -e "s/^[^=]*=//g")
|
||||
GCOV_DIST_DIR=$DIST_DIR
|
||||
shift
|
||||
;;
|
||||
-s)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
SERIAL_NUMBER=$1
|
||||
BUILD_KERNEL=false
|
||||
LAUNCH_CVD=false
|
||||
KILL_CVD=false
|
||||
else
|
||||
echo "device serial is not specified"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--serial*)
|
||||
BUILD_KERNEL=false
|
||||
LAUNCH_CVD=false
|
||||
KILL_CVD=false
|
||||
SERIAL_NUMBER=$(echo $1 | sed -e "s/^[^=]*=//g")
|
||||
shift
|
||||
;;
|
||||
-t)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
TEST_NAME=$1
|
||||
SELECTED_TESTS+="$TEST_NAME "
|
||||
TEST_FILTERS+="--include-filter '$MODULE_NAME $TEST_NAME' "
|
||||
else
|
||||
echo "test name is not specified"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--test*)
|
||||
TEST_NAME=$(echo $1 | sed -e "s/^[^=]*=//g")
|
||||
SELECTED_TESTS+="$TEST_NAME "
|
||||
TEST_FILTERS+="--include-filter '$MODULE_NAME $TEST_NAME'"
|
||||
shift
|
||||
;;
|
||||
--gcov)
|
||||
GCOV=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "unknown argument: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Kernel and modules must be built with the same flags.
|
||||
BUILD_FLAGS=
|
||||
if $GCOV; then
|
||||
BUILD_FLAGS+=" --gcov"
|
||||
fi
|
||||
|
||||
if $BUILD_KERNEL; then
|
||||
echo "Building kernel..."
|
||||
# TODO: add support to build kernel for physical device
|
||||
$BAZEL run $BUILD_FLAGS //common-modules/virtual-device:virtual_device_x86_64_dist -- \
|
||||
--destdir=$DIST_DIR
|
||||
exit_code=$?
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
echo "Command succeeded"
|
||||
else
|
||||
echo "Command failed with exit code $exit_code"
|
||||
exit 1
|
||||
fi
|
||||
exit_code=$?
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
echo "Build kernel succeeded"
|
||||
else
|
||||
echo "Build kernel failed with exit code $exit_code"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if $LAUNCH_CVD; then
|
||||
echo "Launching cvd..."
|
||||
CVD_OUT=$($ACLOUD create --local-kernel-image $DIST_DIR)
|
||||
echo $CVD_OUT
|
||||
INSTANCE_NAME=$(echo "$CVD_OUT" | grep -o "ins-[^\[]*")
|
||||
SERIAL_STRING=$(echo "$CVD_OUT" | grep -oE 'device serial: ([0-9]+\.){3}[0-9]+:[0-9]+')
|
||||
SERIAL_NUMBER=$(echo "$SERIAL_STRING" | sed 's/device serial: //')
|
||||
echo "acloud launched device $SERIAL_NUMBER with instance $INSTANCE_NAME"
|
||||
fi
|
||||
|
||||
if [ -z "$SERIAL_NUMBER" ]; then
|
||||
echo "Device serial is not provided by acloud or by command line flag -s|--serial flag"
|
||||
exit 1
|
||||
else
|
||||
echo "Test with device: $SERIAL_NUMBER"
|
||||
fi
|
||||
|
||||
echo "Get abi from device $SERIAL_NUMBER"
|
||||
ABI=$(adb -s $SERIAL_NUMBER shell getprop ro.product.cpu.abi)
|
||||
echo "Building KUnit tests according to device $SERIAL_NUMBER ro.product.cpu.abi $ABI ..."
|
||||
case $ABI in
|
||||
arm64*)
|
||||
TESTSDIR+="_arm64"
|
||||
$BAZEL run $BUILD_FLAGS //common:kunit_tests_arm64 -- -v --destdir $TESTSDIR
|
||||
;;
|
||||
x86_64*)
|
||||
TESTSDIR+="_x86_64"
|
||||
$BAZEL run $BUILD_FLAGS //common:kunit_tests_x86_64 -- -v --destdir $TESTSDIR
|
||||
;;
|
||||
*)
|
||||
echo "$ABI not supported"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit_code=$?
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
echo "Build kunit succeeded"
|
||||
else
|
||||
echo "Build kunit failed with exit code $exit_code"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$SELECTED_TESTS" ]; then
|
||||
echo "Running all KUnit tests with device $SERIAL_NUMBER..."
|
||||
TEST_FILTERS="--include-filter $MODULE_NAME"
|
||||
else
|
||||
echo "Running $SELECTED_TESTS with device $SERIAL_NUMBER ..."
|
||||
fi
|
||||
|
||||
tf_cli="JAVA_HOME=$JDK_PATH PATH=$JDK_PATH/bin:$PATH $TRADEFED run commandAndExit \
|
||||
template/local_min --template:map test=suite/test_mapping_suite \
|
||||
$TEST_FILTERS --tests-dir=$TESTSDIR --log-file-path=$LOG_DIR \
|
||||
--primary-abi-only -s $SERIAL_NUMBER"
|
||||
|
||||
if $GCOV; then
|
||||
tf_cli+=" --coverage --coverage-toolchain GCOV_KERNEL --auto-collect GCOV_KERNEL_COVERAGE"
|
||||
fi
|
||||
|
||||
echo "Runing tradefed command: $tf_cli"
|
||||
|
||||
eval $tf_cli
|
||||
|
||||
if $LAUNCH_CVD && $KILL_CVD; then
|
||||
echo "Test finished. Deleting cvd instance $INSTANCE_NAME ..."
|
||||
$ACLOUD delete --instance-names $INSTANCE_NAME
|
||||
fi
|
||||
|
||||
if $GCOV; then
|
||||
CREATE_TRACEFILE_CLI="common/tools/testing/android/bin/create-tracefile.py \
|
||||
-t $LOG_DIR -o $LOG_DIR/cov.info"
|
||||
if [ -n "$GCOV_DIST_DIR" ]; then
|
||||
CREATE_TRACEFILE_CLI+=" --dist-dir $GCOV_DIST_DIR"
|
||||
fi
|
||||
echo "Creating tracefile ..."
|
||||
$CREATE_TRACEFILE_CLI && echo "Created tracefile at $LOG_DIR/cov.info"
|
||||
fi
|
||||
@@ -1,98 +0,0 @@
|
||||
HOW TO COLLECT KERNEL CODE COVERAGE FROM A TRADEFED TEST RUN
|
||||
============================================================
|
||||
|
||||
|
||||
## Build and use a kernel with GCOV profile enabled
|
||||
Build your kernel with the [`--gcov`](https://android.googlesource.com/kernel/build/+/refs/heads/main/kleaf/docs/gcov.md) option to enable
|
||||
GCOV profiling from the kernel. This will also trigger the build to save the required *.gcno files needed to viewing the collected count data.
|
||||
|
||||
For example to build a Cuttlefish (CF) kernel with GCOV profiling enabled run:
|
||||
```
|
||||
$ bazel run --gcov //common-modules/virtual-device:virtual_device_x86_64_dist
|
||||
```
|
||||
|
||||
## Run your test(s) using tradefed.sh with kernel coverage collection enabled
|
||||
'tradefed.sh' can be used to run a number of different types of tests. Adding the appropriate coverage flags
|
||||
to the tradefed call will trigger tradefed to take care of mounting debugfs, reseting the gcov counts prior
|
||||
to test run, and the collection of gcov data files from debugfs after test completion.
|
||||
|
||||
These coverage arguments are:
|
||||
```
|
||||
--coverage --coverage-toolchain GCOV_KERNEL --auto-collect GCOV_KERNEL_COVERAGE
|
||||
```
|
||||
|
||||
The following is a full example call running just the `kselftest_net_socket` test in the
|
||||
selftests test suite that exists under the 'bazel-bin/common/testcases' directory. The artifact
|
||||
output has been redirected to 'tf-logs' for easier reference needed in the next step.
|
||||
```
|
||||
$ prebuilts/tradefed/filegroups/tradefed/tradefed.sh run commandAndExit \
|
||||
template/local_min --template:map test=suite/test_mapping_suite \
|
||||
--include-filter 'selftests kselftest_net_socket' --tests-dir=bazel-bin/common/testcases/ \
|
||||
--primary-abi-only --log-file-path tf-logs \
|
||||
--coverage --coverage-toolchain GCOV_KERNEL \
|
||||
--auto-collect GCOV_KERNEL_COVERAGE
|
||||
```
|
||||
|
||||
## Create an lcov tracefile out of the gcov tar artifact from test run
|
||||
The previously mentioned tradefed run will produce a tar file artifact in the
|
||||
tradefed log folder with a name similar to <test>_kernel_coverage_*.tar.gz.
|
||||
This tar file is an archive of all the gcov data files collected into debugfs/
|
||||
from the profiled device. In order to make it easier to work with this data,
|
||||
it needs to be converted to a single lcov tracefile.
|
||||
|
||||
The script 'create-tracefile.py' facilitates this generation by handling the
|
||||
required unpacking, file path corrections and ultimate 'lcov' call.
|
||||
|
||||
An example where we generate a tracefile only including results from net/socket.c.
|
||||
(If no source files are specified as included, then all source file data is used):
|
||||
```
|
||||
$ ./common/tools/testing/android/bin/create-tracefile.py -t tf-logs/ --include net/socket.c
|
||||
```
|
||||
|
||||
This will create a local tracefile named 'cov.info'.
|
||||
|
||||
|
||||
## Visualizing Results
|
||||
With the created tracefile there a number of different ways to view coverage data from it.
|
||||
Check out 'man lcov' for more options.
|
||||
### 1. Text Options
|
||||
#### 1.1 Summary
|
||||
```
|
||||
$ lcov --summary --rc lcov_branch_coverage=1 cov.info
|
||||
Reading tracefile cov.info_fix
|
||||
Summary coverage rate:
|
||||
lines......: 6.0% (81646 of 1370811 lines)
|
||||
functions..: 9.6% (10285 of 107304 functions)
|
||||
branches...: 3.7% (28639 of 765538 branches)
|
||||
```
|
||||
#### 1.2 List
|
||||
```
|
||||
$ lcov --list --rc lcov_branch_coverage=1 cov.info
|
||||
Reading tracefile cov.info_fix
|
||||
|Lines |Functions|Branches
|
||||
Filename |Rate Num|Rate Num|Rate Num
|
||||
================================================================================
|
||||
[/usr/local/google/home/joefradley/dev/common-android-mainline-2/common/]
|
||||
arch/x86/crypto/aesni-intel_glue.c |23.9% 623|22.2% 36|15.0% 240
|
||||
arch/x86/crypto/blake2s-glue.c |50.0% 28|50.0% 2|16.7% 30
|
||||
arch/x86/crypto/chacha_glue.c | 0.0% 157| 0.0% 10| 0.0% 80
|
||||
<truncated>
|
||||
virt/lib/irqbypass.c | 0.0% 137| 0.0% 6| 0.0% 88
|
||||
================================================================================
|
||||
Total:| 6.0% 1369k| 9.6% 0M| 3.7% 764k
|
||||
```
|
||||
### 2. HTML
|
||||
The `lcov` tool `genhtml` is used to generate html. To create html with the default settings:
|
||||
|
||||
```
|
||||
$ genhtml --branch-coverage -o html cov.info
|
||||
```
|
||||
|
||||
The page can be viewed at `html\index.html`.
|
||||
|
||||
Options of interest:
|
||||
* `--frame`: Creates a left hand macro view in a source file view.
|
||||
* `--missed`: Helpful if you want to sort by what source is missing the most as opposed to the default coverage percentages.
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user