diff --git a/tools/testing/android/OWNERS b/tools/testing/android/OWNERS deleted file mode 100644 index fc8a402008c8..000000000000 --- a/tools/testing/android/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -bettyzhou@google.com -edliaw@google.com -hwj@google.com \ No newline at end of file diff --git a/tools/testing/android/bin/acloudb.sh b/tools/testing/android/bin/acloudb.sh deleted file mode 100755 index 40326ca9f8b0..000000000000 --- a/tools/testing/android/bin/acloudb.sh +++ /dev/null @@ -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 "$@" diff --git a/tools/testing/android/bin/create-tracefile.py b/tools/testing/android/bin/create-tracefile.py deleted file mode 100755 index 47233fb636a9..000000000000 --- a/tools/testing/android/bin/create-tracefile.py +++ /dev/null @@ -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/ - 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("", 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("", 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() diff --git a/tools/testing/android/bin/kselftest.sh b/tools/testing/android/bin/kselftest.sh deleted file mode 100755 index 4445daebe2f0..000000000000 --- a/tools/testing/android/bin/kselftest.sh +++ /dev/null @@ -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 diff --git a/tools/testing/android/bin/kunit.sh b/tools/testing/android/bin/kunit.sh deleted file mode 100755 index b043da68bea2..000000000000 --- a/tools/testing/android/bin/kunit.sh +++ /dev/null @@ -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 diff --git a/tools/testing/android/coverage_howto.md b/tools/testing/android/coverage_howto.md deleted file mode 100644 index 00659248d496..000000000000 --- a/tools/testing/android/coverage_howto.md +++ /dev/null @@ -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 _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 - -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. - - -