Remove obsoleted vts/utils/python/
Bug: none
Test: build
Change-Id: I7bf60e3476c805454c8fbd54b52b3452043716e6
diff --git a/utils/python/coverage/Android.mk b/utils/python/coverage/Android.mk
deleted file mode 100644
index a4eb9db..0000000
--- a/utils/python/coverage/Android.mk
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2016 - 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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := vts_coverage_test
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_MODULE_CLASS := FAKE
-LOCAL_IS_HOST_MODULE := true
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-py_scripts := \
- $(LOCAL_PATH)/parser_test.py \
- $(LOCAL_PATH)/gcno_parser_test.py \
- $(LOCAL_PATH)/gcda_parser_test.py \
- $(LOCAL_PATH)/arc_summary_test.py \
- $(LOCAL_PATH)/function_summary_test.py \
- $(LOCAL_PATH)/coverage_report_test.py \
-
-test_dependencies := \
- $(LOCAL_PATH)/testdata/sample.gcno \
- $(LOCAL_PATH)/testdata/sample.gcda
-
-$(LOCAL_BUILT_MODULE): PRIVATE_PY_SCRIPTS := $(py_scripts)
-$(LOCAL_BUILT_MODULE): $(py_scripts) $(test_dependencies)
- @echo "Regression test (build time): $(PRIVATE_MODULE)"
- $(foreach py, $(PRIVATE_PY_SCRIPTS), (PYTHONPATH=$$PYTHONPATH:test python $(py)) &&) true
- $(hide) touch $@
diff --git a/utils/python/coverage/README.md b/utils/python/coverage/README.md
deleted file mode 100644
index 3c11a9b..0000000
--- a/utils/python/coverage/README.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# To run from another Python module
-
-Import the CoverageReport module by including the line:
-
- from vts.utils.python.coverage import CoverageReport
-
-Run the code by calling the parse function as follows:
- html_report = CoverageReport.GenerateCoverageReport(src_file_name, src_file_content, gcov_file_content,
- gcda_file_content)
-
-Args:
- src_file_name: string, the source file name.
- src_file_content: string, the C/C++ source file content.
- gcov_file_content: string, the raw gcov binary file content.
- gcda_file_content: string, the raw gcda binary file content.
-
-Returns:
- the coverage HTML produced for 'src_file_name'.
diff --git a/utils/python/coverage/__init__.py b/utils/python/coverage/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/utils/python/coverage/__init__.py
+++ /dev/null
diff --git a/utils/python/coverage/arc_summary.py b/utils/python/coverage/arc_summary.py
deleted file mode 100644
index ae9138f..0000000
--- a/utils/python/coverage/arc_summary.py
+++ /dev/null
@@ -1,90 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-
-class ArcSummary(object):
- """Summarizes an arc from a .gcno file.
-
- Attributes:
- src_block_index: integer index of the source basic block.
- dstBlockIndex: integer index of the destination basic block.
- on_tree: True iff arc has flag GCOV_ARC_ON_TREE.
- fake: True iff arc has flag GCOV_ARC_FAKE.
- fallthrough: True iff arc has flag GCOV_ARC_FALLTHROUGH.
- resolved: True iff the arc's count has been resolved.
- count: Integer number of times the arc was covered.
- """
-
- GCOV_ARC_ON_TREE = 1
- GCOV_ARC_FAKE = 1 << 1
- GCOV_ARC_FALLTHROUGH = 1 << 2
-
- def __init__(self, src_block, dst_block, flag):
- """Inits the arc summary with provided values.
-
- Stores the source and destination block indices and parses
- the arc flag.
-
- Args:
- src_block: BlockSummary of source block.
- dst_block: BlockSummary of destination block.
- flag: integer flag for the given arc.
- """
-
- self.src_block = src_block
- self.dst_block = dst_block
- self.on_tree = bool(flag & self.GCOV_ARC_ON_TREE)
- self.fake = bool(flag & self.GCOV_ARC_FAKE)
- self.fallthrough = bool(flag & self.GCOV_ARC_FALLTHROUGH)
- self.resolved = False
- self.count = 0
-
- def Resolve(self):
- """Resolves the arc count and returns True if successful.
-
- Uses the property that the sum of counts of arcs entering a
- node is equal to the sum of counts of arcs leaving a node. The
- exception to this rule is fake non-fallthrough nodes, which have
- no exit edges. In this case, remove the arc as an exit arc from
- the source so that the source can be resolved.
-
- Returns:
- True if the arc could be resolved and False otherwise.
- """
- if self.fake and not self.fallthrough:
- try:
- self.src_block.exit_arcs.remove(self)
- except ValueError:
- pass
- elif (len(self.src_block.entry_arcs) > 0 and
- all(a.resolved for a in self.src_block.entry_arcs) and
- all(a.resolved for a in self.src_block.exit_arcs if a != self)):
- in_flow = sum(a.count for a in self.src_block.entry_arcs)
- out_flow = sum(a.count for a in self.src_block.exit_arcs
- if a != self)
- self.count = in_flow - out_flow
- self.resolved = True
- elif (len(self.dst_block.exit_arcs) > 0 and
- all(a.resolved for a in self.dst_block.exit_arcs) and
- all(a.resolved for a in self.dst_block.entry_arcs if a != self)):
- out_flow = sum(a.count for a in self.dst_block.exit_arcs)
- in_flow = sum(a.count for a in self.dst_block.entry_arcs
- if a != self)
- self.count = out_flow - in_flow
- self.resolved = True
- else:
- return False
- return True
diff --git a/utils/python/coverage/arc_summary_test.py b/utils/python/coverage/arc_summary_test.py
deleted file mode 100644
index 6784be7..0000000
--- a/utils/python/coverage/arc_summary_test.py
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2016 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.
-#
-
-import os
-import unittest
-
-from vts.utils.python.coverage import arc_summary
-from vts.utils.python.coverage import block_summary
-
-
-class ArcSummaryTest(unittest.TestCase):
- """Tests for ArcSummary of vts.utils.python.coverage.
- """
-
- def testResolveRemove(self):
- """Verifies that fake, non-fallthrough arc are resolved correctly.
-
- The arc should be removed as an exit arc from the source.
- """
- src = block_summary.BlockSummary(0, 0)
- dst = block_summary.BlockSummary(1, 0)
- flag = arc_summary.ArcSummary.GCOV_ARC_FAKE
- arc = arc_summary.ArcSummary(src, dst, flag)
- src.exit_arcs.append(arc)
- dst.entry_arcs.append(arc)
- self.assertTrue(arc.Resolve())
- self.assertEqual(len(src.exit_arcs), 0)
-
- def testResolveFromSource(self):
- """Verifies that arcs can be resolved from the source.
-
- In the case when the source has fully-resolved entry arcs, the arc
- count should be resolved from the source. I.e. there is only one
- missing arc and it can be solved for from the source.
- """
- middle = block_summary.BlockSummary(-1, 0)
- n = 10
-
- # Create resolved arcs entering the middle block
- for ident in range(n):
- block = block_summary.BlockSummary(ident, 0)
- arc = arc_summary.ArcSummary(block, middle, 0)
- arc.resolved = True
- arc.count = 1
- block.exit_arcs.append(arc)
- middle.entry_arcs.append(arc)
-
- # Create resolved arcs exiting the middle block
- for ident in range(n, 2 * n - 1):
- block = block_summary.BlockSummary(ident, 0)
- arc = arc_summary.ArcSummary(middle, block, 0)
- arc.resolved = True
- arc.count = 1
- block.entry_arcs.append(arc)
- middle.exit_arcs.append(arc)
-
- # Create one unresolved arc exiting the middle
- last = block_summary.BlockSummary(2 * n - 1, 0)
- arc = arc_summary.ArcSummary(middle, last, 0)
- middle.exit_arcs.append(arc)
- last.entry_arcs.append(arc)
- self.assertTrue(arc.Resolve())
- self.assertTrue(arc.resolved)
- self.assertEqual(arc.count, 1)
-
- def testResolveFromDest(self):
- """Verifies that arcs can be resolved from the destination block.
-
- In the case when the source has fully-resolved exit arcs, the arc
- count should be resolved from the source. I.e. there is only one
- missing arc and it can be solved for from the destination.
- """
- middle = block_summary.BlockSummary(-1, 0)
- n = 10
-
- # Create resolved arcs exiting the middle block
- for ident in range(n):
- block = block_summary.BlockSummary(ident, 0)
- arc = arc_summary.ArcSummary(middle, block, 0)
- arc.resolved = True
- arc.count = 1
- block.entry_arcs.append(arc)
- middle.exit_arcs.append(arc)
-
- # Create resolved arcs entering the middle block
- for ident in range(n, 2 * n - 1):
- block = block_summary.BlockSummary(ident, 0)
- arc = arc_summary.ArcSummary(block, middle, 0)
- arc.resolved = True
- arc.count = 1
- block.exit_arcs.append(arc)
- middle.entry_arcs.append(arc)
-
- # Create one unresolved arc entering the middle
- block = block_summary.BlockSummary(2 * n - 1, 0)
- arc = arc_summary.ArcSummary(block, middle, 0)
- middle.entry_arcs.append(arc)
- block.exit_arcs.append(arc)
- self.assertTrue(arc.Resolve())
- self.assertTrue(arc.resolved)
- self.assertEqual(arc.count, 1)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/utils/python/coverage/block_summary.py b/utils/python/coverage/block_summary.py
deleted file mode 100644
index 794ea9b..0000000
--- a/utils/python/coverage/block_summary.py
+++ /dev/null
@@ -1,65 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-
-class BlockSummary(object):
- """Summarizes a block of code from a .gcno file.
-
- Contains all the lines in the block and the entry/exit
- arcs from/to other basic blocks.
-
- Attributes:
- flag: integer flag value.
- entry_arcs: list of ArcSummary objects for each arc
- entering the code block.
- exit_arcs: list of ArcSummary objects for each arc exiting
- the code block.
- lines: list of line numbers represented by the basic block.
- """
-
- def __init__(self, index, flag):
- """Inits the block summary with provided values.
-
- Initializes entryArcs, exitArcs, and lines to the empty list.
- Stores the block flag in the flag attribute.
-
- Args:
- index: the basic block number
- flag: integer block flag.
- """
- self.index = index
- self.flag = flag
- self.entry_arcs = []
- self.exit_arcs = []
- self.count = 0
- self.lines = []
-
- def __str__(self):
- """Serializes the block summary as a string.
-
- Returns:
- String representation of the block.
- """
- output = '\tBlock: %i, Count: %i' % (self.index, self.count)
- if len(self.lines):
- output += ', Lines: ' + ', '.join(str(line) for line in self.lines)
- if len(self.entry_arcs):
- output += ('\r\n\t\t' + str(self.index) + ' <-- ' + ', '.join(
- str(a.src_block.index) for a in self.entry_arcs))
- if len(self.exit_arcs):
- output += ('\r\n\t\t' + str(self.index) + ' --> ' + ', '.join(
- str(a.dst_block.index) for a in self.exit_arcs))
- return output + '\r\n'
diff --git a/utils/python/coverage/coverage_report.py b/utils/python/coverage/coverage_report.py
deleted file mode 100644
index c176e5f..0000000
--- a/utils/python/coverage/coverage_report.py
+++ /dev/null
@@ -1,100 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-"""Generates coverage reports using outputs from GCC.
-
-The GenerateCoverageReport() function returns HTML to display the coverage
-at each line of code in a provided source file. Coverage information is
-parsed from .gcno and .gcda file contents and combined with the source file
-to reconstruct a coverage report. GenerateLineCoverageVector() is a helper
-function that produces a vector of line counts and GenerateCoverageHTML()
-uses the vector and source to produce the HTML coverage report.
-"""
-
-import cgi
-import io
-import logging
-import os
-from vts.utils.python.coverage import gcda_parser
-from vts.utils.python.coverage import gcno_parser
-
-GEN_TAG = "/gen/"
-
-def GenerateLineCoverageVector(gcno_file_summary, exclude_paths, coverage_dict):
- """Process the gcno_file_summary and update the coverage dictionary.
-
- Create a coverage vector for each source file contained in gcno_file_summary
- and update the corresponding item in coverage_dict.
-
- Args:
- gcno_file_summary: FileSummary object after gcno and gcda files have
- been parsed.
- exclude_paths: a list of paths should be ignored in the coverage report.
- coverage_dict: a dictionary for each source file and its corresponding
- coverage vector.
- """
- for ident in gcno_file_summary.functions:
- func = gcno_file_summary.functions[ident]
- file_name = func.src_file_name
- if GEN_TAG in file_name:
- logging.debug("Skip generated source file %s.", file_name)
- continue
- skip_file = False
- for path in exclude_paths:
- if file_name.startswith(path):
- skip_file = True
- break
- if skip_file:
- logging.debug("Skip excluded source file %s.", file_name)
- continue
-
- src_lines_counts = coverage_dict[file_name] if file_name in coverage_dict else []
- for block in func.blocks:
- for line in block.lines:
- if line > len(src_lines_counts):
- src_lines_counts.extend([-1] *
- (line - len(src_lines_counts)))
- if src_lines_counts[line - 1] < 0:
- src_lines_counts[line - 1] = 0
- src_lines_counts[line - 1] += block.count
- coverage_dict[file_name] = src_lines_counts
-
-
-def GetCoverageStats(src_lines_counts):
- """Returns the coverage stats.
-
- Args:
- src_lines_counts: A list of non-negative integers or -1 representing
- the number of times the i-th line was executed.
- -1 indicates a line that is not executable.
-
- Returns:
- integer, the number of lines instrumented for coverage measurement
- integer, the number of executed or covered lines
- """
- total = 0
- covered = 0
- if not src_lines_counts or not isinstance(src_lines_counts, list):
- logging.error("GetCoverageStats: input invalid.")
- return total, covered
-
- for line in src_lines_counts:
- if line < 0:
- continue
- total += 1
- if line > 0:
- covered += 1
- return total, covered
-
diff --git a/utils/python/coverage/coverage_report_test.py b/utils/python/coverage/coverage_report_test.py
deleted file mode 100644
index 049d84c..0000000
--- a/utils/python/coverage/coverage_report_test.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2016 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.
-#
-
-import os
-import unittest
-
-from vts.utils.python.coverage import gcno_parser
-from vts.utils.python.coverage import gcda_parser
-from vts.utils.python.coverage import coverage_report
-
-
-class CoverageReportTest(unittest.TestCase):
- """Unit tests for CoverageReport of vts.utils.python.coverage.
- """
-
- GOLDEN_GCNO_PATH = 'testdata/sample.gcno'
- GOLDEN_GCDA_PATH = 'testdata/sample.gcda'
-
- @classmethod
- def setUpClass(cls):
- dir_path = os.path.dirname(os.path.realpath(__file__))
- gcno_path = os.path.join(dir_path, cls.GOLDEN_GCNO_PATH)
- with open(gcno_path, 'rb') as file:
- gcno_summary = gcno_parser.GCNOParser(file).Parse()
- gcda_path = os.path.join(dir_path, cls.GOLDEN_GCDA_PATH)
- with open(gcda_path, 'rb') as file:
- parser = gcda_parser.GCDAParser(file)
- parser.Parse(gcno_summary)
- cls.gcno_summary = gcno_summary
-
- def testGenerateLineCoverageVector(self):
- """Tests that coverage vector is correctly generated.
-
- Runs GenerateLineCoverageVector on sample file and checks
- result.
- """
- coverage_dict = dict()
- exclude_paths = []
- src_lines_counts = coverage_report.GenerateLineCoverageVector(
- self.gcno_summary, exclude_paths, coverage_dict)
- expected = {'sample.c': [-1, -1, -1, -1, 2, -1, -1, -1, -1, -1, 2,
- 2, 2, -1, 2, -1, 2, 0, -1, 2, -1, -1, 2, 2, 502,
- 500, -1, -1, 2, -1, 2, -1, -1, -1, 2, -1,
- -1, -1, -1, 2, 2, 2]}
- self.assertEqual(coverage_dict, expected)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/utils/python/coverage/coverage_utils.py b/utils/python/coverage/coverage_utils.py
deleted file mode 100644
index 217b028..0000000
--- a/utils/python/coverage/coverage_utils.py
+++ /dev/null
@@ -1,771 +0,0 @@
-#
-# Copyright (C) 2016 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.
-import argparse
-import io
-import json
-import logging
-import os
-import shutil
-import sys
-import time
-import zipfile
-
-from vts.proto import VtsReportMessage_pb2 as ReportMsg
-from vts.runners.host import keys
-from vts.utils.python.archive import archive_parser
-from vts.utils.python.common import cmd_utils
-from vts.utils.python.controllers.adb import AdbError
-from vts.utils.python.coverage import coverage_report
-from vts.utils.python.coverage import gcda_parser
-from vts.utils.python.coverage import gcno_parser
-from vts.utils.python.coverage.parser import FileFormatError
-from vts.utils.python.os import path_utils
-from vts.utils.python.web import feature_utils
-
-FLUSH_PATH_VAR = "GCOV_PREFIX" # environment variable for gcov flush path
-TARGET_COVERAGE_PATH = "/data/misc/trace/" # location to flush coverage
-LOCAL_COVERAGE_PATH = "/tmp/vts-test-coverage" # location to pull coverage to host
-
-# Environment for test process
-COVERAGE_TEST_ENV = "GCOV_PREFIX_OVERRIDE=true GCOV_PREFIX=/data/misc/trace/self"
-
-GCNO_SUFFIX = ".gcno"
-GCDA_SUFFIX = ".gcda"
-COVERAGE_SUFFIX = ".gcnodir"
-GIT_PROJECT = "git_project"
-MODULE_NAME = "module_name"
-NAME = "name"
-PATH = "path"
-GEN_TAG = "/gen/"
-
-_BUILD_INFO = "BUILD_INFO" # name of build info artifact
-_GCOV_ZIP = "gcov.zip" # name of gcov artifact zip
-_REPO_DICT = "repo-dict" # name of dictionary from project to revision in BUILD_INFO
-
-_CLEAN_TRACE_COMMAND = "rm -rf /data/misc/trace/*"
-_FLUSH_COMMAND = (
- "GCOV_PREFIX_OVERRIDE=true GCOV_PREFIX=/data/local/tmp/flusher "
- "/data/local/tmp/vts_coverage_configure flush")
-_SP_COVERAGE_PATH = "self" # relative location where same-process coverage is dumped.
-
-_CHECKSUM_GCNO_DICT = "checksum_gcno_dict"
-_COVERAGE_ZIP = "coverage_zip"
-_REVISION_DICT = "revision_dict"
-
-
-class CoverageFeature(feature_utils.Feature):
- """Feature object for coverage functionality.
-
- Attributes:
- enabled: boolean, True if coverage is enabled, False otherwise
- web: (optional) WebFeature, object storing web feature util for test run
- local_coverage_path: path to store the coverage files.
- _device_resource_dict: a map from device serial number to host resources directory.
- _hal_names: the list of hal names for which to process coverage.
- _coverage_report_file_prefix: prefix of the output coverage report file.
- """
-
- _TOGGLE_PARAM = keys.ConfigKeys.IKEY_ENABLE_COVERAGE
- _REQUIRED_PARAMS = [keys.ConfigKeys.IKEY_ANDROID_DEVICE]
- _OPTIONAL_PARAMS = [
- keys.ConfigKeys.IKEY_MODULES,
- keys.ConfigKeys.IKEY_OUTPUT_COVERAGE_REPORT,
- keys.ConfigKeys.IKEY_GLOBAL_COVERAGE,
- keys.ConfigKeys.IKEY_EXCLUDE_COVERAGE_PATH,
- keys.ConfigKeys.IKEY_COVERAGE_REPORT_PATH,
- ]
-
- _DEFAULT_EXCLUDE_PATHS = [
- "bionic", "external/libcxx", "system/core", "system/libhidl",
- "system/libfmq"
- ]
-
- def __init__(self, user_params, web=None):
- """Initializes the coverage feature.
-
- Args:
- user_params: A dictionary from parameter name (String) to parameter value.
- web: (optional) WebFeature, object storing web feature util for test run
- local_coverage_path: (optional) path to store the .gcda files and coverage reports.
- """
- self.ParseParameters(self._TOGGLE_PARAM, self._REQUIRED_PARAMS,
- self._OPTIONAL_PARAMS, user_params)
- self.web = web
- self._device_resource_dict = {}
- self._hal_names = None
-
- timestamp_seconds = str(int(time.time() * 1000000))
- self.local_coverage_path = os.path.join(LOCAL_COVERAGE_PATH,
- timestamp_seconds)
- if os.path.exists(self.local_coverage_path):
- logging.debug("removing existing coverage path: %s",
- self.local_coverage_path)
- shutil.rmtree(self.local_coverage_path)
- os.makedirs(self.local_coverage_path)
-
- self._coverage_report_dir = getattr(
- self, keys.ConfigKeys.IKEY_COVERAGE_REPORT_PATH, None)
-
- self._coverage_report_file_prefix = ""
-
- self.global_coverage = getattr(
- self, keys.ConfigKeys.IKEY_GLOBAL_COVERAGE, True)
- if self.enabled:
- android_devices = getattr(self,
- keys.ConfigKeys.IKEY_ANDROID_DEVICE)
- if not isinstance(android_devices, list):
- logging.warn("Android device information not available.")
- self.enabled = False
- for device in android_devices:
- serial = device.get(keys.ConfigKeys.IKEY_SERIAL)
- coverage_resource_path = device.get(
- keys.ConfigKeys.IKEY_GCOV_RESOURCES_PATH)
- if not serial:
- logging.error("Missing serial information in device: %s",
- device)
- continue
- if not coverage_resource_path:
- logging.error(
- "Missing coverage resource path in device: %s", device)
- continue
- self._device_resource_dict[str(serial)] = str(
- coverage_resource_path)
-
- if self.enabled:
- logging.info("Coverage is enabled")
- else:
- logging.debug("Coverage is disabled.")
-
- def _FindGcnoSummary(self, gcda_file_path, gcno_file_parsers):
- """Find the corresponding gcno summary for given gcda file.
-
- Identify the corresponding gcno summary for given gcda file from a list
- of gcno files with the same checksum as the gcda file by matching
- the the gcda file path.
- Note: if none of the gcno summary contains the source file same as the
- given gcda_file_path (e.g. when the corresponding source file does not
- contain any executable codes), just return the last gcno summary in the
- list as a fall back solution.
-
- Args:
- gcda_file_path: the path of gcda file (without extensions).
- gcno_file_parsers: a list of gcno file parser that has the same
- chechsum.
-
- Returns:
- The corresponding gcno summary for given gcda file.
- """
- gcno_summary = None
- # For each gcno files with the matched checksum, compare the
- # gcda_file_path to find the corresponding gcno summary.
- for gcno_file_parser in gcno_file_parsers:
- try:
- gcno_summary = gcno_file_parser.Parse()
- except FileFormatError:
- logging.error("Error parsing gcno for gcda %s", gcda_file_path)
- break
- legacy_build = "soong/.intermediates" not in gcda_file_path
- for key in gcno_summary.functions:
- src_file_path = gcno_summary.functions[key].src_file_name
- src_file_name = src_file_path.rsplit(".", 1)[0]
- # If build with legacy compile system, compare only the base
- # source file name. Otherwise, compare the full source file name
- # (with path info).
- if legacy_build:
- base_src_file_name = os.path.basename(src_file_name)
- if gcda_file_path.endswith(base_src_file_name):
- return gcno_summary
- else:
- if gcda_file_path.endswith(src_file_name):
- return gcno_summary
- # If no gcno file matched with the gcda_file_name, return the last
- # gcno summary as a fall back solution.
- return gcno_summary
-
- def _GetChecksumGcnoDict(self, cov_zip):
- """Generates a dictionary from gcno checksum to GCNOParser object.
-
- Processes the gcnodir files in the zip file to produce a mapping from gcno
- checksum to the GCNOParser object wrapping the gcno content.
- Note there might be multiple gcno files corresponds to the same checksum.
-
- Args:
- cov_zip: the zip file containing gcnodir files from the device build
-
- Returns:
- the dictionary of gcno checksums to GCNOParser objects
- """
- checksum_gcno_dict = dict()
- fnames = cov_zip.namelist()
- instrumented_modules = [
- f for f in fnames if f.endswith(COVERAGE_SUFFIX)
- ]
- for instrumented_module in instrumented_modules:
- # Read the gcnodir file
- archive = archive_parser.Archive(
- cov_zip.open(instrumented_module).read())
- try:
- archive.Parse()
- except ValueError:
- logging.error("Archive could not be parsed: %s", name)
- continue
-
- for gcno_file_path in archive.files:
- gcno_stream = io.BytesIO(archive.files[gcno_file_path])
- gcno_file_parser = gcno_parser.GCNOParser(gcno_stream)
- if gcno_file_parser.checksum in checksum_gcno_dict:
- checksum_gcno_dict[gcno_file_parser.checksum].append(
- gcno_file_parser)
- else:
- checksum_gcno_dict[gcno_file_parser.checksum] = [
- gcno_file_parser
- ]
- return checksum_gcno_dict
-
- def _ClearTargetGcov(self, dut, serial, path_suffix=None):
- """Removes gcov data from the device.
-
- Finds and removes all gcda files relative to TARGET_COVERAGE_PATH.
- Args:
- dut: the device under test.
- path_suffix: optional string path suffix.
- """
- path = TARGET_COVERAGE_PATH
- if path_suffix:
- path = path_utils.JoinTargetPath(path, path_suffix)
- self._ExecuteOneAdbShellCommand(dut, serial, _CLEAN_TRACE_COMMAND)
-
- def _GetHalPids(self, dut, hal_names):
- """Get the process id for the given hal names.
-
- Args:
- dut: the device under test.
- hal_names: list of strings for targeting hal names.
-
- Returns:
- list of strings for the corresponding pids.
- """
- logging.debug("hal_names: %s", str(hal_names))
- searchString = "|".join(hal_names)
- entries = []
- try:
- dut.rootAdb()
- entries = dut.adb.shell(
- "lshal -itp 2> /dev/null | grep -E \"{0}\"".format(
- searchString)).splitlines()
- except AdbError as e:
- logging.error("failed to get pid entries")
-
- pids = set(pid.strip()
- for pid in map(lambda entry: entry.split()[-1], entries)
- if pid.isdigit())
- return pids
-
- def InitializeDeviceCoverage(self, dut=None, serial=None):
- """Initializes the device for coverage before tests run.
-
- Flushes, then finds and removes all gcda files under
- TARGET_COVERAGE_PATH before tests run.
-
- Args:
- dut: the device under test.
- """
- self._ExecuteOneAdbShellCommand(dut, serial, "setenforce 0")
- self._ExecuteOneAdbShellCommand(dut, serial, _FLUSH_COMMAND)
- logging.debug("Removing existing gcda files.")
- self._ClearTargetGcov(dut, serial)
-
- # restart HALs to include coverage for initialization code.
- if self._hal_names:
- pids = self._GetHalPids(dut, self._hal_names)
- for pid in pids:
- cmd = "kill -9 " + pid
- self._ExecuteOneAdbShellCommand(dut, serial, cmd)
-
- def _GetGcdaDict(self, dut, serial):
- """Retrieves GCDA files from device and creates a dictionary of files.
-
- Find all GCDA files on the target device, copy them to the host using
- adb, then return a dictionary mapping from the gcda basename to the
- temp location on the host.
-
- Args:
- dut: the device under test.
-
- Returns:
- A dictionary with gcda basenames as keys and contents as the values.
- """
- logging.debug("Creating gcda dictionary")
- gcda_dict = {}
- logging.debug("Storing gcda tmp files to: %s",
- self.local_coverage_path)
-
- self._ExecuteOneAdbShellCommand(dut, serial, _FLUSH_COMMAND)
-
- gcda_files = set()
- if self._hal_names:
- pids = self._GetHalPids(dut, self._hal_names)
- pids.add(_SP_COVERAGE_PATH)
- for pid in pids:
- path = path_utils.JoinTargetPath(TARGET_COVERAGE_PATH, pid)
- try:
- files = dut.adb.shell("find %s -name \"*.gcda\"" % path)
- gcda_files.update(files.split("\n"))
- except AdbError as e:
- logging.info("No gcda files found in path: \"%s\"", path)
- else:
- cmd = ("find %s -name \"*.gcda\"" % TARGET_COVERAGE_PATH)
- result = self._ExecuteOneAdbShellCommand(dut, serial, cmd)
- if result:
- gcda_files.update(result.split("\n"))
-
- for gcda in gcda_files:
- if gcda:
- basename = os.path.basename(gcda.strip())
- file_name = os.path.join(self.local_coverage_path, basename)
- if dut is None:
- results = cmd_utils.ExecuteShellCommand(
- "adb -s %s pull %s %s " % (serial, gcda, file_name))
- if (results[cmd_utils.EXIT_CODE][0]):
- logging.error(
- "Fail to execute command: %s. error: %s" %
- (cmd, str(results[cmd_utils.STDERR][0])))
- else:
- dut.adb.pull("%s %s" % (gcda, file_name))
- gcda_content = open(file_name, "rb").read()
- gcda_dict[gcda.strip()] = gcda_content
- self._ClearTargetGcov(dut, serial)
- return gcda_dict
-
- def _OutputCoverageReport(self, isGlobal, coverage_report_msg=None):
- logging.info("Outputing coverage data")
- timestamp_seconds = str(int(time.time() * 1000000))
- coverage_report_file_name = "coverage_report_" + timestamp_seconds + ".txt"
- if self._coverage_report_file_prefix:
- coverage_report_file_name = "coverage_report_" + self._coverage_report_file_prefix + ".txt"
-
- coverage_report_file = None
- if (self._coverage_report_dir):
- if not os.path.exists(self._coverage_report_dir):
- os.makedirs(self._coverage_report_dir)
- coverage_report_file = os.path.join(self._coverage_report_dir,
- coverage_report_file_name)
- else:
- coverage_report_file = os.path.join(self.local_coverage_path,
- coverage_report_file_name)
-
- logging.info("Storing coverage report to: %s", coverage_report_file)
- if self.web and self.web.enabled:
- coverage_report_msg = ReportMsg.TestReportMessage()
- if isGlobal:
- for c in self.web.report_msg.coverage:
- coverage = coverage_report_msg.coverage.add()
- coverage.CopyFrom(c)
- else:
- for c in self.web.current_test_report_msg.coverage:
- coverage = coverage_report_msg.coverage.add()
- coverage.CopyFrom(c)
- if coverage_report_msg is not None:
- with open(coverage_report_file, "w+") as f:
- f.write(str(coverage_report_msg))
-
- def _AutoProcess(self, cov_zip, revision_dict, gcda_dict, isGlobal):
- """Process coverage data and appends coverage reports to the report message.
-
- Matches gcno files with gcda files and processes them into a coverage report
- with references to the original source code used to build the system image.
- Coverage information is appended as a CoverageReportMessage to the provided
- report message.
-
- Git project information is automatically extracted from the build info and
- the source file name enclosed in each gcno file. Git project names must
- resemble paths and may differ from the paths to their project root by at
- most one. If no match is found, then coverage information will not be
- be processed.
-
- e.g. if the project path is test/vts, then its project name may be
- test/vts or <some folder>/test/vts in order to be recognized.
-
- Args:
- cov_zip: the ZipFile object containing the gcno coverage artifacts.
- revision_dict: the dictionary from project name to project version.
- gcda_dict: the dictionary of gcda basenames to gcda content (binary string)
- isGlobal: boolean, True if the coverage data is for the entire test, False if only for
- the current test case.
- """
- checksum_gcno_dict = self._GetChecksumGcnoDict(cov_zip)
- output_coverage_report = getattr(
- self, keys.ConfigKeys.IKEY_OUTPUT_COVERAGE_REPORT, False)
- exclude_coverage_path = getattr(
- self, keys.ConfigKeys.IKEY_EXCLUDE_COVERAGE_PATH, [])
- for idx, path in enumerate(exclude_coverage_path):
- base_name = os.path.basename(path)
- if base_name and "." not in base_name:
- path = path if path.endswith("/") else path + "/"
- exclude_coverage_path[idx] = path
- exclude_coverage_path.extend(self._DEFAULT_EXCLUDE_PATHS)
-
- coverage_dict = dict()
- coverage_report_message = ReportMsg.TestReportMessage()
-
- for gcda_name in gcda_dict:
- if GEN_TAG in gcda_name:
- # skip coverage measurement for intermediate code.
- logging.warn("Skip for gcda file: %s", gcda_name)
- continue
-
- gcda_stream = io.BytesIO(gcda_dict[gcda_name])
- gcda_file_parser = gcda_parser.GCDAParser(gcda_stream)
- file_name = gcda_name.rsplit(".", 1)[0]
-
- if not gcda_file_parser.checksum in checksum_gcno_dict:
- logging.info("No matching gcno file for gcda: %s", gcda_name)
- continue
- gcno_file_parsers = checksum_gcno_dict[gcda_file_parser.checksum]
- gcno_summary = self._FindGcnoSummary(file_name, gcno_file_parsers)
- if gcno_summary is None:
- logging.error("No gcno file found for gcda %s.", gcda_name)
- continue
-
- # Process and merge gcno/gcda data
- try:
- gcda_file_parser.Parse(gcno_summary)
- except FileFormatError:
- logging.error("Error parsing gcda file %s", gcda_name)
- continue
-
- coverage_report.GenerateLineCoverageVector(
- gcno_summary, exclude_coverage_path, coverage_dict)
-
- for src_file_path in coverage_dict:
- # Get the git project information
- # Assumes that the project name and path to the project root are similar
- revision = None
- for project_name in revision_dict:
- # Matches cases when source file root and project name are the same
- if src_file_path.startswith(str(project_name)):
- git_project_name = str(project_name)
- git_project_path = str(project_name)
- revision = str(revision_dict[project_name])
- logging.debug("Source file '%s' matched with project '%s'",
- src_file_path, git_project_name)
- break
-
- parts = os.path.normpath(str(project_name)).split(os.sep, 1)
- # Matches when project name has an additional prefix before the
- # project path root.
- if len(parts) > 1 and src_file_path.startswith(parts[-1]):
- git_project_name = str(project_name)
- git_project_path = parts[-1]
- revision = str(revision_dict[project_name])
- logging.debug("Source file '%s' matched with project '%s'",
- src_file_path, git_project_name)
- break
-
- if not revision:
- logging.info("Could not find git info for %s", src_file_path)
- continue
-
- coverage_vec = coverage_dict[src_file_path]
- total_count, covered_count = coverage_report.GetCoverageStats(
- coverage_vec)
- if self.web and self.web.enabled:
- self.web.AddCoverageReport(coverage_vec, src_file_path,
- git_project_name, git_project_path,
- revision, covered_count,
- total_count, isGlobal)
- else:
- coverage = coverage_report_message.coverage.add()
- coverage.total_line_count = total_count
- coverage.covered_line_count = covered_count
- coverage.line_coverage_vector.extend(coverage_vec)
-
- src_file_path = os.path.relpath(src_file_path,
- git_project_path)
- coverage.file_path = src_file_path
- coverage.revision = revision
- coverage.project_name = git_project_name
-
- if output_coverage_report:
- self._OutputCoverageReport(isGlobal, coverage_report_message)
-
- # TODO: consider to deprecate the manual process.
- def _ManualProcess(self, cov_zip, revision_dict, gcda_dict, isGlobal):
- """Process coverage data and appends coverage reports to the report message.
-
- Opens the gcno files in the cov_zip for the specified modules and matches
- gcno/gcda files. Then, coverage vectors are generated for each set of matching
- gcno/gcda files and appended as a CoverageReportMessage to the provided
- report message. Unlike AutoProcess, coverage information is only processed
- for the modules explicitly defined in 'modules'.
-
- Args:
- cov_zip: the ZipFile object containing the gcno coverage artifacts.
- revision_dict: the dictionary from project name to project version.
- gcda_dict: the dictionary of gcda basenames to gcda content (binary string)
- isGlobal: boolean, True if the coverage data is for the entire test, False if only for
- the current test case.
- """
- output_coverage_report = getattr(
- self, keys.ConfigKeys.IKEY_OUTPUT_COVERAGE_REPORT, True)
- modules = getattr(self, keys.ConfigKeys.IKEY_MODULES, None)
- covered_modules = set(cov_zip.namelist())
- for module in modules:
- if MODULE_NAME not in module or GIT_PROJECT not in module:
- logging.error(
- "Coverage module must specify name and git project: %s",
- module)
- continue
- project = module[GIT_PROJECT]
- if PATH not in project or NAME not in project:
- logging.error("Project name and path not specified: %s",
- project)
- continue
-
- name = str(module[MODULE_NAME]) + COVERAGE_SUFFIX
- git_project = str(project[NAME])
- git_project_path = str(project[PATH])
-
- if name not in covered_modules:
- logging.error("No coverage information for module %s", name)
- continue
- if git_project not in revision_dict:
- logging.error(
- "Git project not present in device revision dict: %s",
- git_project)
- continue
-
- revision = str(revision_dict[git_project])
- archive = archive_parser.Archive(cov_zip.open(name).read())
- try:
- archive.Parse()
- except ValueError:
- logging.error("Archive could not be parsed: %s", name)
- continue
-
- for gcno_file_path in archive.files:
- file_name_path = gcno_file_path.rsplit(".", 1)[0]
- file_name = os.path.basename(file_name_path)
- gcno_content = archive.files[gcno_file_path]
- gcno_stream = io.BytesIO(gcno_content)
- try:
- gcno_summary = gcno_parser.GCNOParser(gcno_stream).Parse()
- except FileFormatError:
- logging.error("Error parsing gcno file %s", gcno_file_path)
- continue
- src_file_path = None
-
- # Match gcno file with gcda file
- gcda_name = file_name + GCDA_SUFFIX
- if gcda_name not in gcda_dict:
- logging.error("No gcda file found %s.", gcda_name)
- continue
-
- src_file_path = self._ExtractSourceName(
- gcno_summary, file_name)
-
- if not src_file_path:
- logging.error("No source file found for %s.",
- gcno_file_path)
- continue
-
- # Process and merge gcno/gcda data
- gcda_content = gcda_dict[gcda_name]
- gcda_stream = io.BytesIO(gcda_content)
- try:
- gcda_parser.GCDAParser(gcda_stream).Parse(gcno_summary)
- except FileFormatError:
- logging.error("Error parsing gcda file %s", gcda_content)
- continue
-
- if self.web and self.web.enabled:
- coverage_vec = coverage_report.GenerateLineCoverageVector(
- src_file_path, gcno_summary)
- total_count, covered_count = coverage_report.GetCoverageStats(
- coverage_vec)
- self.web.AddCoverageReport(coverage_vec, src_file_path,
- git_project, git_project_path,
- revision, covered_count,
- total_count, isGlobal)
-
- if output_coverage_report:
- self._OutputCoverageReport(isGlobal)
-
- def SetCoverageData(self, dut=None, serial=None, isGlobal=False):
- """Sets and processes coverage data.
-
- Organizes coverage data and processes it into a coverage report in the
- current test case
-
- Requires feature to be enabled; no-op otherwise.
-
- Args:
- dut: the device object for which to pull coverage data
- isGlobal: True if the coverage data is for the entire test, False if
- if the coverage data is just for the current test case.
- """
- if not self.enabled:
- return
-
- if serial is None:
- serial = "default" if dut is None else dut.adb.shell(
- "getprop ro.serialno").strip()
-
- if not serial in self._device_resource_dict:
- logging.error("Invalid device provided: %s", serial)
- return
-
- resource_path = self._device_resource_dict[serial]
- if not resource_path:
- logging.error("Coverage resource path not found.")
- return
-
- gcda_dict = self._GetGcdaDict(dut, serial)
- logging.debug("Coverage file paths %s", str([fp for fp in gcda_dict]))
-
- cov_zip = zipfile.ZipFile(os.path.join(resource_path, _GCOV_ZIP))
-
- revision_dict = json.load(
- open(os.path.join(resource_path, _BUILD_INFO)))[_REPO_DICT]
-
- if not hasattr(self, keys.ConfigKeys.IKEY_MODULES):
- # auto-process coverage data
- self._AutoProcess(cov_zip, revision_dict, gcda_dict, isGlobal)
- else:
- # explicitly process coverage data for the specified modules
- self._ManualProcess(cov_zip, revision_dict, gcda_dict, isGlobal)
-
- # cleanup the downloaded gcda files.
- logging.debug("Cleaning up gcda files.")
- files = os.listdir(self.local_coverage_path)
- for item in files:
- if item.endswith(".gcda"):
- os.remove(os.path.join(self.local_coverage_path, item))
-
- def SetHalNames(self, names=[]):
- """Sets the HAL names for which to process coverage.
-
- Args:
- names: list of strings, names of hal (e.g. android.hardware.light@2.0)
- """
- self._hal_names = list(names)
-
- def SetCoverageReportFilePrefix(self, prefix):
- """Sets the prefix for outputting the coverage report file.
-
- Args:
- prefix: strings, prefix of the coverage report file.
- """
- self._coverage_report_file_prefix = prefix
-
- def SetCoverageReportDirectory(self, corverage_report_dir):
- """Sets the path for storing the coverage report file.
-
- Args:
- corverage_report_dir: strings, dir to store the coverage report file.
- """
- self._coverage_report_dir = corverage_report_dir
-
- def _ExecuteOneAdbShellCommand(self, dut, serial, cmd):
- """Helper method to execute a shell command and return results.
-
- Args:
- dut: the device under test.
- cmd: string, command to execute.
- Returns:
- stdout result of the command, None if command fails.
- """
- if dut is None:
- results = cmd_utils.ExecuteShellCommand("adb -s %s shell %s" %
- (serial, cmd))
- if (results[cmd_utils.EXIT_CODE][0]):
- logging.error("Fail to execute command: %s. error: %s" %
- (cmd, str(results[cmd_utils.STDERR][0])))
- return None
- else:
- return results[cmd_utils.STDOUT][0]
- else:
- try:
- return dut.adb.shell(cmd)
- except AdbError as e:
- logging.warn("Fail to execute command: %s. error: %s" %
- (cmd, str(e)))
- return None
-
-
-if __name__ == '__main__':
- """ Tools to process coverage data.
-
- Usage:
- python coverage_utils.py operation [--serial=device_serial_number]
- [--report_prefix=prefix_of_coverage_report]
-
- Example:
- python coverage_utils.py init_coverage
- python coverage_utils.py get_coverage --serial HT7821A00243
- python coverage_utils.py get_coverage --serial HT7821A00243 --report_prefix=test
- """
- logging.basicConfig(level=logging.INFO)
- parser = argparse.ArgumentParser(description="Coverage process tool.")
- parser.add_argument(
- "--report_prefix",
- dest="report_prefix",
- required=False,
- help="Prefix of the coverage report.")
- parser.add_argument(
- "--report_path",
- dest="report_path",
- required=False,
- help="directory to store the coverage reports.")
- parser.add_argument(
- "--serial", dest="serial", required=True, help="Device serial number.")
- parser.add_argument(
- "--gcov_rescource_path",
- dest="gcov_rescource_path",
- required=True,
- help="Directory that stores gcov resource files.")
- parser.add_argument(
- "operation",
- help=
- "Operation for processing coverage data, e.g. 'init_coverage', get_coverage'"
- )
- args = parser.parse_args()
-
- if args.operation != "init_coverage" and args.operation != "get_coverage":
- print "Unsupported operation. Exiting..."
- sys.exit(1)
- user_params = {
- keys.ConfigKeys.IKEY_ENABLE_COVERAGE:
- True,
- keys.ConfigKeys.IKEY_ANDROID_DEVICE: [{
- keys.ConfigKeys.IKEY_SERIAL:
- args.serial,
- keys.ConfigKeys.IKEY_GCOV_RESOURCES_PATH:
- args.gcov_rescource_path,
- }],
- keys.ConfigKeys.IKEY_OUTPUT_COVERAGE_REPORT:
- True,
- keys.ConfigKeys.IKEY_GLOBAL_COVERAGE:
- True
- }
- coverage = CoverageFeature(user_params)
- if args.operation == "init_coverage":
- coverage.InitializeDeviceCoverage(serial=args.serial)
- elif args.operation == "get_coverage":
- if args.report_prefix:
- coverage.SetCoverageReportFilePrefix(args.report_prefix)
- if args.report_path:
- coverage.SetCoverageReportDirectory(args.report_path)
- coverage.SetCoverageData(serial=args.serial, isGlobal=True)
diff --git a/utils/python/coverage/file_summary.py b/utils/python/coverage/file_summary.py
deleted file mode 100644
index 211188a..0000000
--- a/utils/python/coverage/file_summary.py
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-
-class FileSummary(object):
- """Summarizes structure and coverage information from GCC output.
-
- Represents the data in .gcno and .gcda files.
-
- Attributes:
- functions: Dictionary of FunctionSummary objects for each function described
- in a GCNO file (key: integer ident, value: FunctionSummary object)
- """
-
- def __init__(self):
- """Inits the object with an empty list in its functions attribute.
- """
- self.functions = {}
-
- def __str__(self):
- """Serializes the summary as a string.
-
- Returns:
- String representation of the functions, blocks, arcs, and lines.
- """
- output = 'Coverage Summary:\r\n'
- for ident in self.functions:
- output += str(self.functions[ident])
- return output
diff --git a/utils/python/coverage/function_summary.py b/utils/python/coverage/function_summary.py
deleted file mode 100644
index 3cf89ea..0000000
--- a/utils/python/coverage/function_summary.py
+++ /dev/null
@@ -1,102 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-
-class FunctionSummary(object):
- """Summarizes a function and its blocks from .gcno file.
-
- Attributes:
- blocks: list of BlockSummary objects for each block in the function.
- ident: integer function identifier.
- name: function name.
- src_file_name: name of source file containing the function.
- first_line_number: integer line number at which the function begins in
- srcFile.
- """
-
- def __init__(self, ident, name, src_file_name, first_line_number):
- """Inits the function summary with provided values.
-
- Stores the identification string, name, source file name, and
- first line number in the object attributes. Initializes the block
- attribute to the empty list.
-
- Args:
- ident: integer function identifier.
- name: function name.
- src_file_name: name of source file containing the function.
- first_line_number: integer line number at which the function begins in
- the source file.
- """
- self.blocks = []
- self.ident = ident
- self.name = name
- self.src_file_name = src_file_name
- self.first_line_number = first_line_number
-
- def Resolve(self):
- """Resolves the block and arc counts.
-
- Using the edges that were resolved by the GCDA file,
- counts are resolved in the unresolved arcs. Then, block
- counts are resolved by summing the counts along arcs entering
- the block.
-
- Returns:
- True if the counts could be resolved and False otherwise.
- """
-
- unresolved_arcs = []
- for block in self.blocks:
- for arc in block.exit_arcs:
- if not arc.resolved:
- unresolved_arcs.append(arc)
-
- index = 0
- prev_length = len(unresolved_arcs) + 1
- # Resolve the arc counts
- while len(unresolved_arcs) > 0:
- index = index % len(unresolved_arcs)
- if index == 0 and len(unresolved_arcs) == prev_length:
- return False
- else:
- prev_length = len(unresolved_arcs)
- arc = unresolved_arcs[index]
- if arc.Resolve():
- unresolved_arcs.remove(arc)
- else:
- index = index + 1
-
- # Resolve the block counts
- for block in self.blocks:
- if len(block.entry_arcs):
- block.count = sum(arc.count for arc in block.entry_arcs)
- else:
- block.count = sum(arc.count for arc in block.exit_arcs)
-
- return True
-
- def __str__(self):
- """Serializes the function summary as a string.
-
- Returns:
- String representation of the functions and its blocks.
- """
- output = ('Function: %s : %s\r\n\tFirst Line Number:%i\r\n' %
- (self.src_file_name, self.name, self.first_line_number))
- for b in self.blocks:
- output += str(b)
- return output
diff --git a/utils/python/coverage/function_summary_test.py b/utils/python/coverage/function_summary_test.py
deleted file mode 100644
index 16ea4db..0000000
--- a/utils/python/coverage/function_summary_test.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2016 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.
-#
-
-import os
-import unittest
-
-from vts.utils.python.coverage import arc_summary
-from vts.utils.python.coverage import block_summary
-from vts.utils.python.coverage import function_summary
-
-
-class FunctionSummaryTest(unittest.TestCase):
- """Tests for FunctionSummary of vts.utils.python.coverage.
- """
-
- def setUp(self):
- """Creates a function summary and a chain of blocks.
-
- Creates an arc between adjacent blocks. All arcs are left with default
- values (unresolved, count of 0).
- """
- self.n = 10
- self.count = 5
- self.function_summary = function_summary.FunctionSummary(0, 'test',
- 'test.c', 0)
- self.function_summary.blocks = [block_summary.BlockSummary(i, 0)
- for i in range(self.n)]
- self.arcs = []
- for i in range(1, self.n):
- arc = arc_summary.ArcSummary(self.function_summary.blocks[i - 1],
- self.function_summary.blocks[i], 0)
- self.function_summary.blocks[i - 1].exit_arcs.append(arc)
- self.function_summary.blocks[i].entry_arcs.append(arc)
- self.arcs.append(arc)
-
- def testResolveChainStart(self):
- """Tests for correct resolution in a long chain.
-
- Test resolution for the case when there is a chain of unresolved arcs
- after a resolved arc.
- """
- self.arcs[0].resolved = True
- self.arcs[0].count = self.count
-
- self.function_summary.Resolve()
- for arc in self.arcs:
- self.assertTrue(arc.resolved)
- self.assertEqual(self.count, arc.count)
- self.assertEqual(self.count, arc.src_block.count)
- self.assertEqual(self.count, arc.dst_block.count)
-
- def testResolveChainEnd(self):
- """Tests for correct resolution in a long chain.
-
- Test resolution for the case when there is a chain of unresolved arcs
- before a resolved arc.
- """
- self.arcs[-1].resolved = True
- self.arcs[-1].count = self.count
-
- self.function_summary.Resolve()
- for arc in self.arcs:
- self.assertTrue(arc.resolved)
- self.assertEqual(self.count, arc.count)
- self.assertEqual(self.count, arc.src_block.count)
- self.assertEqual(self.count, arc.dst_block.count)
-
- def testResolveFailure(self):
- """Tests for failure when no progress can be made.
-
- Test that Resolve() returns False when there is not enough information
- to resolve any arcs.
- """
- self.assertFalse(self.function_summary.Resolve())
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/utils/python/coverage/gcda_parser.py b/utils/python/coverage/gcda_parser.py
deleted file mode 100644
index b2dbc3c..0000000
--- a/utils/python/coverage/gcda_parser.py
+++ /dev/null
@@ -1,192 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-"""Parses the contents of a GCDA file generated by the GCC compiler.
-
-The parse() function updates a summary object, which was created by
-the GCNO parser, and includes coverage information along arcs and at
-code blocks.
-
-
- Typical usage example:
-
- parse(file_name, file_summary)
-"""
-
-import struct
-import sys
-
-from vts.utils.python.coverage import parser
-from vts.utils.python.coverage import gcno_parser
-
-class GCDAParser(parser.GcovStreamParserUtil):
- """Parser object class stores stateful information for parsing GCDA files.
-
- Stores the file stream and a FileSummary object as it is updated.
-
- Attributes:
- checksum: The checksum (int) of the file
- file_summary: The FileSummary object describing the source file
- format: Character denoting the endianness of the file
- stream: File stream object for a GCDA file
- """
-
- MAGIC = 0x67636461
- TAG_FUNCTION = 0x01000000
- TAG_COUNTER = 0x01a10000
- TAG_OBJECT = 0xa1000000
- TAG_PROGRAM = 0xa3000000
-
- def __init__(self, stream):
- """Inits the parser with the input stream and default values.
-
- The byte order is set by default to little endian and the summary file
- must be provided from the output of the GCNOparser.
-
- Args:
- stream: An input binary file stream to a .gcno file
- """
- self._file_summary = None
- super(GCDAParser, self).__init__(stream, self.MAGIC)
-
- @property
- def file_summary(self):
- """Gets the FileSummary object where coverage data is stored.
-
- Returns:
- A FileSummary object.
- """
- return self._file_summary
-
- @file_summary.setter
- def file_summary(self, file_summary):
- """Sets the FileSummary object in which to store coverage data.
-
- Args:
- file_summary: A FileSummary object from a processed gcno file
- """
- self._file_summary = file_summary
-
- def Parse(self, file_summary):
- """Runs the parser on the file opened in the stream attribute.
-
- Reads coverage information from the GCDA file stream and resolves
- block and edge weights.
-
- Returns:
- FileSummary object representing the coverage for functions, blocks,
- arcs, and lines in the opened GCNO file.
-
- Raises:
- parser.FileFormatError: invalid file format or invalid counts.
- """
- self.file_summary = file_summary
- func = None
-
- while True:
- tag = str()
-
- try:
- while True:
- tag = self.ReadInt()
- if (tag == self.TAG_FUNCTION or tag == self.TAG_COUNTER or
- tag == self.TAG_OBJECT or tag == self.TAG_PROGRAM):
- break
- length = self.ReadInt()
- except parser.FileFormatError:
- return self.file_summary # end of file reached
-
- if tag == self.TAG_FUNCTION:
- func = self.ReadFunction(length)
- elif tag == self.TAG_COUNTER:
- self.ReadCounts(func)
- if not func.Resolve():
- raise parser.FileFormatError(
- "Corrupt file: Counts could not be resolved.")
- elif tag == self.TAG_OBJECT:
- pass
- elif tag == self.TAG_PROGRAM:
- self.ReadInt() # checksum
- for i in range(length - 1):
- self.ReadInt()
-
- def ReadFunction(self, length):
- """Reads a function header from the stream.
-
- Reads information about a function from the gcda file stream and
- returns the function.
-
- Args:
- func: the function for which coverage information will be read.
-
- Raises:
- parser.FileFormatError: Corrupt file.
- """
- ident = self.ReadInt()
- func = self.file_summary.functions[ident]
- checksum = self.ReadInt()
- words_read = 3
- if int(self.version[1]) > 4:
- self.ReadInt()
- words_read = 4
-
- if words_read < length:
- gcda_name = self.ReadString()
-
- return func
-
- def ReadCounts(self, func):
- """Reads arc counts from the stream.
-
- Reads counts from the gcda file stream for arcs that are not
- fake and are not in the tree. Updates their counts and marks them
- as having resolved counts.
-
- Args:
- func: FunctionSummary for which arc counts will be read.
- """
- for block in func.blocks:
- for arc in block.exit_arcs:
- if not arc.fake and not arc.on_tree:
- count = self.ReadInt64()
- arc.count = count
- arc.resolved = True
-
-
-def ParseGcdaFile(file_name, file_summary):
- """Parses the .gcno file specified by the input.
-
- Reads the .gcno file specified and parses the information describing
- basic blocks, functions, and arcs.
-
- Args:
- file_name: A string file path to a .gcno file
- file_summary: The summary from a parsed gcno file
-
- Returns:
- A summary object containing information about the coverage for each
- block in each function.
- """
-
- with open(file_name, 'rb') as stream:
- return GCDAParser(stream).Parse(file_summary)
-
-
-if __name__ == '__main__':
- if len(sys.argv) != 2:
- print('usage: gcda_parser.py [gcda file name] [gcno file name]')
- else:
- file_summary = gcno_parser.ParseGcnoFile(sys.argv[2])
- print(str(ParseGcdaFile(sys.argv[1], file_summary)))
diff --git a/utils/python/coverage/gcda_parser_test.py b/utils/python/coverage/gcda_parser_test.py
deleted file mode 100644
index df1b4b0..0000000
--- a/utils/python/coverage/gcda_parser_test.py
+++ /dev/null
@@ -1,144 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2016 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.
-#
-import os
-import unittest
-
-from vts.utils.python.coverage import arc_summary
-from vts.utils.python.coverage import block_summary
-from vts.utils.python.coverage import file_summary
-from vts.utils.python.coverage import function_summary
-from vts.utils.python.coverage import gcda_parser
-from vts.utils.python.coverage import gcno_parser
-from vts.utils.python.coverage.parser_test import MockStream
-
-
-class GCDAParserTest(unittest.TestCase):
- """Tests for GCDA parser of vts.utils.python.coverage.
- """
-
- GOLDEN_GCNO_PATH = 'testdata/sample.gcno'
- GOLDEN_GCDA_PATH = 'testdata/sample.gcda'
-
- def setUp(self):
- """Creates a stream for each test.
- """
- self.stream = MockStream(gcda_parser.GCDAParser.MAGIC)
-
- def testReadFunction(self):
- """Verifies that the correct function is read and returned.
- """
- ident = 100
- checksum = 0
- fs = file_summary.FileSummary()
- func = function_summary.FunctionSummary(ident, 'test', 'test.c', 0)
- fs.functions[ident] = func
- self.stream = MockStream.concat_int(self.stream, ident)
- self.stream = MockStream.concat_int(self.stream, 0)
- self.stream = MockStream.concat_int(self.stream, 0)
- self.stream = MockStream.concat_string(self.stream, 'test')
- length = 5
- parser = gcda_parser.GCDAParser(self.stream)
- parser.file_summary = fs
- func = parser.ReadFunction(5)
- assert (func.ident == ident)
-
- def testReadCountsNormal(self):
- """Verifies that counts are read correctly.
-
- Verifies that arcs are marked as resolved and count is correct.
- """
- n = 5
- fs = file_summary.FileSummary()
- func = function_summary.FunctionSummary(0, 'test', 'test.c', 0)
- blocks = [block_summary.BlockSummary(i, 0) for i in range(n)]
- func.blocks = blocks
- fs.functions[func.ident] = func
- for i in range(1, n):
- arc = arc_summary.ArcSummary(blocks[0], blocks[i], 0)
- blocks[0].exit_arcs.append(arc)
- blocks[i].entry_arcs.append(arc)
- self.stream = MockStream.concat_int64(self.stream, i)
- parser = gcda_parser.GCDAParser(self.stream)
- parser.file_summary = fs
- parser.ReadCounts(func)
- for i, arc in zip(range(1, n), blocks[0].exit_arcs):
- self.assertEqual(i, arc.count)
- self.assertTrue(arc.resolved)
-
- def testReadCountsFakeOrOnTree(self):
- """Verifies that counts are read correctly when there are skipped arcs.
-
- Verifies that the fake arc and the arc on the tree are skipped while other
- arcs are read and resolved correctly.
- """
- n = 10
- fs = file_summary.FileSummary()
- func = function_summary.FunctionSummary(0, 'test', 'test.c', 0)
- blocks = [block_summary.BlockSummary(i, 0) for i in range(n)]
- func.blocks = blocks
- fs.functions[func.ident] = func
-
- arc = arc_summary.ArcSummary(blocks[0], blocks[1],
- arc_summary.ArcSummary.GCOV_ARC_FAKE)
- blocks[0].exit_arcs.append(arc)
- blocks[1].entry_arcs.append(arc)
-
- arc = arc_summary.ArcSummary(blocks[0], blocks[2],
- arc_summary.ArcSummary.GCOV_ARC_ON_TREE)
- blocks[0].exit_arcs.append(arc)
- blocks[2].entry_arcs.append(arc)
-
- for i in range(3, n):
- arc = arc_summary.ArcSummary(blocks[0], blocks[i], 0)
- blocks[0].exit_arcs.append(arc)
- blocks[i].entry_arcs.append(arc)
- self.stream = MockStream.concat_int64(self.stream, i)
-
- parser = gcda_parser.GCDAParser(self.stream)
- parser.file_summary = fs
- parser.ReadCounts(func)
- self.assertFalse(blocks[0].exit_arcs[0].resolved)
- self.assertFalse(blocks[0].exit_arcs[1].resolved)
- for i, arc in zip(range(3, n), blocks[0].exit_arcs[2:]):
- self.assertEqual(i, arc.count)
- self.assertTrue(arc.resolved)
-
- def testSampleFile(self):
- """Asserts correct parsing of sample GCDA file.
-
- Verifies the block coverage counts for each function.
- """
- dir_path = os.path.dirname(os.path.realpath(__file__))
- gcno_path = os.path.join(dir_path, self.GOLDEN_GCNO_PATH)
- gcda_path = os.path.join(dir_path, self.GOLDEN_GCDA_PATH)
- summary = gcno_parser.ParseGcnoFile(gcno_path)
- gcda_parser.ParseGcdaFile(gcda_path, summary)
- # Function: main
- expected_list = [2, 0, 2, 2, 2, 0, 2, 2, 500, 502, 2, 2]
- for index, expected in zip(range(len(expected_list)), expected_list):
- self.assertEqual(summary.functions[3].blocks[index].count,
- expected)
-
- # Function: testFunctionName
- expected_list = [2, 2, 2, 2, 2]
- for index, expected in zip(range(len(expected_list)), expected_list):
- self.assertEqual(summary.functions[4].blocks[index].count,
- expected)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/utils/python/coverage/gcno_parser.py b/utils/python/coverage/gcno_parser.py
deleted file mode 100644
index 62bafd9..0000000
--- a/utils/python/coverage/gcno_parser.py
+++ /dev/null
@@ -1,242 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-"""Parses the contents of a GCNO file generated by the GCC compiler.
-
-The parse() function returns a FileSummary object, which
-contains descriptions of all functions in the parsed .gcno file. Each
-FunctionSummary object describes the code blocks within each function,
-the line numbers associated within each block, and the arcs exiting/entering
-each block.
-
-
- Typical usage example:
-
- summary = parse(file_name)
-"""
-
-import math
-import struct
-import sys
-
-from vts.utils.python.coverage import arc_summary
-from vts.utils.python.coverage import block_summary
-from vts.utils.python.coverage import file_summary
-from vts.utils.python.coverage import function_summary
-from vts.utils.python.coverage import parser
-
-
-class GCNOParser(parser.GcovStreamParserUtil):
- """Parser object class stores stateful information for parsing GCNO file.
-
- Stores the file stream and summary object as it is updated.
-
- Attributes:
- checksum: The checksum (int) of the file
- file_summary: The FileSummary object describing the GCNO file
- format: Character denoting the endianness of the file
- parsed: True if the content has been parsed, False otherwise
- stream: File stream object for a GCNO file
- version: The (integer) version of the GCNO file
- """
-
- MAGIC = 0x67636e6f
- TAG_FUNCTION = 0x01000000
- TAG_BLOCKS = 0x01410000
- TAG_ARCS = 0x01430000
- TAG_LINES = 0x01450000
- BYTES_IN_WORD = 4
- HEADER_LENGTH = 3 # number of words in a section header
-
- def __init__(self, stream):
- """Inits the parser with the input stream and default values.
-
- The byte order is set by default to little endian and the summary file
- is instantiated with an empty FileSummary object.
-
- Args:
- stream: An input binary file stream to a .gcno file
- """
- super(GCNOParser, self).__init__(stream, self.MAGIC)
- self.file_summary = file_summary.FileSummary()
- self.parsed = False
-
- def Parse(self):
- """Runs the parser on the file opened in the stream attribute.
-
- Reads the binary file and extracts functions, blocks, arcs, and
- lines. Information is stored the summary attribute.
-
- Returns:
- FileSummary object representing the functions, blocks, arcs,
- and lines in the opened GCNO file.
-
- Raises:
- parser.FileFormatError: invalid file format.
- """
- if self.parsed:
- return self.file_summary
-
- func = None
-
- while True:
- tag = str()
-
- try:
- while True:
- tag = self.ReadInt()
- if (tag == self.TAG_FUNCTION or tag == self.TAG_BLOCKS or
- tag == self.TAG_ARCS or tag == self.TAG_LINES):
- break
- length = self.ReadInt()
- except parser.FileFormatError:
- if not func:
- raise parser.FileFormatError("Invalid file.")
- self.file_summary.functions[func.ident] = func
- self.parsed = True
- return self.file_summary # end of file reached
-
- if tag == self.TAG_FUNCTION:
- if func:
- self.file_summary.functions[func.ident] = func
- func = self.ReadFunction()
-
- elif tag == self.TAG_BLOCKS:
- self.ReadBlocks(length, func)
-
- elif tag == self.TAG_ARCS:
- self.ReadArcs(length, func)
-
- elif tag == self.TAG_LINES:
- self.ReadLines(length, func)
-
- def ReadFunction(self):
- """Reads and returns a function from the stream.
-
- Reads information about a function from the gcno file stream and
- returns a summary object.
-
- Returns:
- FunctionSummary object containing the function name, source file,
- and first line number.
-
- Raises:
- parser.FileFormatError: Function could not be read.
- """
- ident = self.ReadInt()
- self.ReadInt() # line number checksum
- if int(self.version[1]) > 4:
- self.ReadInt() # configuration checksum
- name = self.ReadString()
- source_file_name = self.ReadString()
- first_line_number = self.ReadInt()
- return function_summary.FunctionSummary(ident, name, source_file_name,
- first_line_number)
-
- def ReadBlocks(self, length, func):
- """Reads the basic block information from the stream.
-
- Reads information about the basic blocks from the gcno file
- stream and updates the specified function.
-
- Args:
- length: number of blocks to read
- func: FunctionSummary object for the blocks' parent function
-
- Raises:
- parser.FileFormatError: Blocks could not be read. Corrupt file.
- """
-
- blocks = []
- for _ in range(length):
- block_flag = self.ReadInt()
- block = block_summary.BlockSummary(len(blocks), block_flag)
- blocks.append(block)
- func.blocks.extend(blocks)
-
- def ReadArcs(self, length, func):
- """Reads the arcs from the stream.
-
- Parses the arcs from the gcno file and updates the input
- function summary with arc information.
-
- Args:
- length: represents the number of bytes to read
- func: FunctionSummary object for the arcs' parent fuction
-
- Raises:
- parser.FileFormatError: Arcs could not be read. Corrupt file.
- """
-
- src_block_index = self.ReadInt()
- src_block = func.blocks[src_block_index]
- n_arcs = (length - 1) / 2
- arcs = []
- for _ in range(n_arcs):
- dst_block_index = self.ReadInt()
- dst_block = func.blocks[dst_block_index]
- flag = self.ReadInt()
- arc = arc_summary.ArcSummary(src_block, dst_block, flag)
- src_block.exit_arcs.append(arc)
- dst_block.entry_arcs.append(arc)
-
- def ReadLines(self, length, func):
- """Reads the line information from the stream.
-
- Parses the lines from the gcno file and updates the input
- function summary with line information.
-
- Args:
- length: represents the number of bytes to read
- func: FunctionSummary object for the lines' parent fuction
-
- Raises:
- parser.FileFormatError: Lines could not be read. Corrupt file.
- """
-
- block_number = self.ReadInt()
- self.ReadInt()
- lines = []
- src = self.ReadString() # source file name
- src_length = int(math.ceil(len(src) * 1.0 / self.BYTES_IN_WORD)) + 1
- for i in range(length - src_length - self.HEADER_LENGTH):
- line = self.ReadInt()
- if line:
- lines.append(line)
- func.blocks[block_number].lines = lines
-
-
-def ParseGcnoFile(file_name):
- """Parses the .gcno file specified by the input.
-
- Reads the .gcno file specified and parses the information describing
- basic blocks, functions, and arcs.
-
- Args:
- file_name: A string file path to a .gcno file
-
- Returns:
- A FileSummary object containing information about all of the
- fuctions, blocks, and arcs in the .gcno file.
- """
-
- with open(file_name, 'rb') as stream:
- return GCNOParser(stream).Parse()
-
-if __name__ == '__main__':
- if len(sys.argv) < 3 or sys.argv[1] != '-f':
- print('usage: gcno_parser.py -f [file name]')
- else:
- print(str(ParseGcnoFile(sys.argv[2])))
diff --git a/utils/python/coverage/gcno_parser_test.py b/utils/python/coverage/gcno_parser_test.py
deleted file mode 100644
index 9918817..0000000
--- a/utils/python/coverage/gcno_parser_test.py
+++ /dev/null
@@ -1,219 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2016 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.
-#
-
-import math
-import os
-import struct
-import unittest
-
-from vts.utils.python.coverage import arc_summary
-from vts.utils.python.coverage import block_summary
-from vts.utils.python.coverage import function_summary
-from vts.utils.python.coverage import gcno_parser
-from vts.utils.python.coverage.parser_test import MockStream
-
-
-class GCNOParserTest(unittest.TestCase):
- """Tests for GCNO parser of vts.utils.python.coverage.
-
- Ensures error handling, byte order detection, and correct
- parsing of functions, blocks, arcs, and lines.
- """
-
- GOLDEN_GCNO_PATH = 'testdata/sample.gcno'
-
- def setUp(self):
- """Creates a stream for each test.
- """
- self.stream = MockStream()
-
- def testReadFunction(self):
- """Asserts that the function is read correctly.
-
- Verifies that ident, name, source file name,
- and first line number are all read correctly.
- """
- ident = 102010
- self.stream = MockStream.concat_int(self.stream, ident)
- self.stream = MockStream.concat_int(self.stream, 0)
- self.stream = MockStream.concat_int(self.stream, 0)
- name = "TestFunction"
- src_file_name = "TestSouceFile.c"
- first_line_number = 102
- self.stream = MockStream.concat_string(self.stream, name)
- self.stream = MockStream.concat_string(self.stream, src_file_name)
- self.stream = MockStream.concat_int(self.stream, first_line_number)
- parser = gcno_parser.GCNOParser(self.stream)
- summary = parser.ReadFunction()
- self.assertEqual(name, summary.name)
- self.assertEqual(ident, summary.ident)
- self.assertEqual(src_file_name, summary.src_file_name)
- self.assertEqual(first_line_number, summary.first_line_number)
-
- def testReadBlocks(self):
- """Asserts that blocks are correctly read from the stream.
-
- Tests correct values for flag and index.
- """
- n_blocks = 10
- func = function_summary.FunctionSummary(0, "func", "src.c", 1)
- for i in range(n_blocks):
- self.stream = MockStream.concat_int(self.stream, 3 * i)
- parser = gcno_parser.GCNOParser(self.stream)
- parser.ReadBlocks(n_blocks, func)
- self.assertEqual(len(func.blocks), n_blocks)
- for i in range(n_blocks):
- self.assertEqual(func.blocks[i].flag, 3 * i)
- self.assertEqual(func.blocks[i].index, i)
-
- def testReadArcsNormal(self):
- """Asserts that arcs are correctly read from the stream.
-
- Does not test the use of flags. Validates that arcs are
- created in both blocks and the source/destination are
- correct for each.
- """
- n_blocks = 50
- func = function_summary.FunctionSummary(0, "func", "src.c", 1)
- func.blocks = [block_summary.BlockSummary(i, 3 * i)
- for i in range(n_blocks)]
- src_block_index = 0
- skip = 2
- self.stream = MockStream.concat_int(self.stream, src_block_index)
- for i in range(src_block_index + 1, n_blocks, skip):
- self.stream = MockStream.concat_int(self.stream, i)
- self.stream = MockStream.concat_int(
- self.stream, 0) # no flag applied to the arc
- parser = gcno_parser.GCNOParser(self.stream)
- n_arcs = len(range(src_block_index + 1, n_blocks, skip))
- parser.ReadArcs(n_arcs * 2 + 1, func)
- j = 0
- for i in range(src_block_index + 1, n_blocks, skip):
- self.assertEqual(
- func.blocks[src_block_index].exit_arcs[j].src_block.index,
- src_block_index)
- self.assertEqual(
- func.blocks[src_block_index].exit_arcs[j].dst_block.index, i)
- self.assertEqual(func.blocks[i].entry_arcs[0].src_block.index,
- src_block_index)
- self.assertEqual(func.blocks[i].entry_arcs[0].dst_block.index, i)
- j += 1
-
- def testReadArcFlags(self):
- """Asserts that arc flags are correctly interpreted.
- """
- n_blocks = 5
- func = function_summary.FunctionSummary(0, "func", "src.c", 1)
- func.blocks = [block_summary.BlockSummary(i, 3 * i)
- for i in range(n_blocks)]
- self.stream = MockStream.concat_int(self.stream,
- 0) # source block index
-
- self.stream = MockStream.concat_int(self.stream, 1) # normal arc
- self.stream = MockStream.concat_int(self.stream, 0)
-
- self.stream = MockStream.concat_int(self.stream, 2) # on-tree arc
- self.stream = MockStream.concat_int(
- self.stream, arc_summary.ArcSummary.GCOV_ARC_ON_TREE)
-
- self.stream = MockStream.concat_int(self.stream, 3) # fake arc
- self.stream = MockStream.concat_int(
- self.stream, arc_summary.ArcSummary.GCOV_ARC_FAKE)
-
- self.stream = MockStream.concat_int(self.stream, 4) # fallthrough arc
- self.stream = MockStream.concat_int(
- self.stream, arc_summary.ArcSummary.GCOV_ARC_FALLTHROUGH)
-
- parser = gcno_parser.GCNOParser(self.stream)
- parser.ReadArcs(4 * 2 + 1, func)
-
- self.assertFalse(func.blocks[0].exit_arcs[0].on_tree)
- self.assertFalse(func.blocks[0].exit_arcs[0].fake)
- self.assertFalse(func.blocks[0].exit_arcs[0].fallthrough)
-
- self.assertTrue(func.blocks[0].exit_arcs[1].on_tree)
- self.assertFalse(func.blocks[0].exit_arcs[1].fake)
- self.assertFalse(func.blocks[0].exit_arcs[1].fallthrough)
-
- self.assertFalse(func.blocks[0].exit_arcs[2].on_tree)
- self.assertTrue(func.blocks[0].exit_arcs[2].fake)
- self.assertFalse(func.blocks[0].exit_arcs[2].fallthrough)
-
- self.assertFalse(func.blocks[0].exit_arcs[3].on_tree)
- self.assertFalse(func.blocks[0].exit_arcs[3].fake)
- self.assertTrue(func.blocks[0].exit_arcs[3].fallthrough)
-
- def testReadLines(self):
- """Asserts that lines are read correctly.
-
- Blocks must have correct references to the lines contained
- in the block.
- """
- self.stream = MockStream.concat_int(self.stream, 2) # block number
- self.stream = MockStream.concat_int(self.stream, 0) # dummy
- name = "src.c"
- name_length = int(
- math.ceil(1.0 * len(name) / MockStream.BYTES_PER_WORD)) + 1
- self.stream = MockStream.concat_string(self.stream, name)
- n_arcs = 5
- for i in range(1, n_arcs + 1):
- self.stream = MockStream.concat_int(self.stream, i)
-
- n_blocks = 5
- func = function_summary.FunctionSummary(0, "func", name, 1)
- func.blocks = [block_summary.BlockSummary(i, 3 * i)
- for i in range(n_blocks)]
- parser = gcno_parser.GCNOParser(self.stream)
- parser.ReadLines(n_arcs + name_length + 3, func)
- self.assertEqual(len(func.blocks[2].lines), 5)
- self.assertEqual(func.blocks[2].lines, range(1, 6))
-
- def testSampleFile(self):
- """Asserts correct parsing of sample GCNO file.
-
- Verifies the blocks and lines for each function in
- the file.
- """
- dir_path = os.path.dirname(os.path.realpath(__file__))
- file_path = os.path.join(dir_path, self.GOLDEN_GCNO_PATH)
- summary = gcno_parser.ParseGcnoFile(file_path)
- self.assertEqual(len(summary.functions), 2)
-
- # Check function: testFunctionName
- func = summary.functions[4]
- self.assertEqual(func.name, 'testFunctionName')
- self.assertEqual(func.src_file_name, 'sample.c')
- self.assertEqual(func.first_line_number, 35)
- self.assertEqual(len(func.blocks), 5)
- expected_list = [[], [], [35, 40, 41], [42], []]
- for index, expected in zip(range(5), expected_list):
- self.assertEqual(func.blocks[index].lines, expected)
-
- # Check function: main
- func = summary.functions[3]
- self.assertEqual(func.name, 'main')
- self.assertEqual(func.first_line_number, 5)
- self.assertEqual(len(func.blocks), 12)
- self.assertEqual(func.blocks[0].lines, [])
- expected_list = [[], [], [5, 11, 12, 13], [15], [17], [18], [20],
- [23, 24, 25], [26, 25], [], [29], [31]]
- for index, expected in zip(range(12), expected_list):
- self.assertEqual(func.blocks[index].lines, expected)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/utils/python/coverage/parser.py b/utils/python/coverage/parser.py
deleted file mode 100644
index fbee873..0000000
--- a/utils/python/coverage/parser.py
+++ /dev/null
@@ -1,116 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-"""Generic parser class for reading GCNO and GCDA files.
-
-Implements read functions for strings, 32-bit integers, and
-64-bit integers.
-"""
-
-import struct
-
-
-class FileFormatError(Exception):
- """Exception for invalid file format.
-
- Thrown when an unexpected value type is read from the file stream
- or when the end of file is reached unexpectedly."""
-
- pass
-
-
-class GcovStreamParserUtil(object):
- """Parser object for storing the stream and format information.
-
- Attributes:
- stream: File stream object for a GCNO file
- format: Character denoting the endianness of the file
- checksum: The checksum (int) of the file
- """
-
- def __init__(self, stream, magic):
- """Inits the parser with the input stream.
-
- The byte order is set by default to little endian and the summary file
- is instantiated with an empty GCNOSummary object.
-
- Args:
- stream: An input binary file stream to a .gcno file
- gcno_summary: The summary from a parsed gcno file
- """
- self.stream = stream
- self.format = '<'
-
- tag = self.ReadInt()
- self.version = ''.join(
- struct.unpack(self.format + 'ssss', self.stream.read(4)))
- self.checksum = self.ReadInt()
-
- if tag != magic:
- tag = struct.unpack('>I', struct.pack('<I', tag))[0]
- if tag == magic: # switch endianness
- self.format = '>'
- else:
- raise FileFormatError('Invalid file format.')
-
- def ReadInt(self):
- """Reads and returns an integer from the stream.
-
- Returns:
- A 4-byte integer from the stream attribute.
-
- Raises:
- FileFormatError: Corrupt file.
- """
- try:
- return struct.unpack(self.format + 'I', self.stream.read(4))[0]
- except (TypeError, ValueError, struct.error) as error:
- raise FileFormatError('Corrupt file.')
-
- def ReadInt64(self):
- """Reads and returns a 64-bit integer from the stream.
-
- Returns:
- An 8-byte integer from the stream attribute.
-
- Raises:
- FileFormatError: Corrupt file.
- """
- lo = self.ReadInt()
- hi = self.ReadInt()
- return (hi << 32) | lo
-
- def ReadString(self):
- """Reads and returns a string from the stream.
-
- First reads an integer denoting the number of words to read,
- then reads and returns the string with trailing padding characters
- stripped.
-
- Returns:
- A string from the stream attribute.
-
- Raises:
- FileFormatError: End of file reached.
- """
- length = self.ReadInt() << 2
- if length > 0:
- try:
- return ''.join(
- struct.unpack(self.format + 's' * length, self.stream.read(
- length))).rstrip('\x00')
- except (TypeError, ValueError, struct.error):
- raise FileFormatError('Corrupt file.')
- return str()
diff --git a/utils/python/coverage/parser_test.py b/utils/python/coverage/parser_test.py
deleted file mode 100644
index 0916f9a..0000000
--- a/utils/python/coverage/parser_test.py
+++ /dev/null
@@ -1,216 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2016 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.
-#
-
-import math
-import os
-import struct
-import unittest
-
-from vts.utils.python.coverage import parser
-
-MAGIC = 0x67636e6f
-
-
-class MockStream(object):
- """MockStream object allows for mocking file reading behavior.
-
- Allows for adding integers and strings to the file stream in a
- specified byte format and then reads them as if from a file.
-
- Attributes:
- content: the byte list representing a file stream
- cursor: the index into the content such that everything before it
- has been read already.
- """
- BYTES_PER_WORD = 4
-
- def __init__(self, magic=MAGIC, format='<'):
- self.format = format
- self.magic = magic
- version = struct.unpack(format + 'I', '*802')[0]
- self.content = struct.pack(format + 'III', magic, version, 0)
- self.cursor = 0
-
- @classmethod
- def concat_int(cls, stream, integer):
- """Returns the stream with a binary formatted integer concatenated.
-
- Args:
- stream: the stream to which the integer will be concatenated.
- integer: the integer to be concatenated to the content stream.
- format: the string format decorator to apply to the integer.
-
- Returns:
- The content with the binary-formatted integer concatenated.
- """
- new_content = stream.content + struct.pack(stream.format + 'I',
- integer)
- s = MockStream(stream.magic, stream.format)
- s.content = new_content
- s.cursor = stream.cursor
- return s
-
- @classmethod
- def concat_int64(cls, stream, integer):
- """Returns the stream with a binary formatted int64 concatenated.
-
- Args:
- stream: the stream to which the integer will be concatenated.
- integer: the 8-byte int to be concatenated to the content stream.
- format: the string format decorator to apply to the long.
-
- Returns:
- The content with the binary-formatted int64 concatenated.
- """
- lo = ((1 << 32) - 1) & integer
- hi = (integer - lo) >> 32
- new_content = stream.content + struct.pack(stream.format + 'II', lo,
- hi)
- s = MockStream(stream.magic, stream.format)
- s.content = new_content
- s.cursor = stream.cursor
- return s
-
- @classmethod
- def concat_string(cls, stream, string):
- """Returns the stream with a binary formatted string concatenated.
-
- Preceeds the string with an integer representing the number of
- words in the string. Pads the string so that it is word-aligned.
-
- Args:
- stream: the stream to which the string will be concatenated.
- string: the string to be concatenated to the content stream.
- format: the string format decorator to apply to the integer.
-
- Returns:
- The content with the formatted binary string concatenated.
- """
- byte_count = len(string)
- word_count = int(
- math.ceil(byte_count * 1.0 / MockStream.BYTES_PER_WORD))
- padding = '\x00' * (
- MockStream.BYTES_PER_WORD * word_count - byte_count)
- new_content = stream.content + struct.pack(
- stream.format + 'I', word_count) + bytes(string + padding)
- s = MockStream(stream.magic, stream.format)
- s.content = new_content
- s.cursor = stream.cursor
- return s
-
- def read(self, n_bytes):
- """Reads the specified number of bytes from the content stream.
-
- Args:
- n_bytes: integer number of bytes to read.
-
- Returns:
- The string of length n_bytes beginning at the cursor location
- in the content stream.
- """
- content = self.content[self.cursor:self.cursor + n_bytes]
- self.cursor += n_bytes
- return content
-
-
-class ParserTest(unittest.TestCase):
- """Tests for stream parser of vts.utils.python.coverage.
-
- Ensures error handling, byte order detection, and correct
- parsing of integers and strings.
- """
-
- def setUp(self):
- """Creates a stream for each test.
- """
- self.stream = MockStream()
-
- def testLittleEndiannessInitialization(self):
- """Tests parser init with little-endian byte order.
-
- Verifies that the byte-order is correctly detected.
- """
- p = parser.GcovStreamParserUtil(self.stream, MAGIC)
- self.assertEqual(p.format, '<')
-
- def testBigEndiannessInitialization(self):
- """Tests parser init with big-endian byte order.
-
- Verifies that the byte-order is correctly detected.
- """
- self.stream = MockStream(format='>')
- p = parser.GcovStreamParserUtil(self.stream, MAGIC)
- self.assertEqual(p.format, '>')
-
- def testReadIntNormal(self):
- """Asserts that integers are correctly read from the stream.
-
- Tests the normal case--when the value is actually an integer.
- """
- integer = 2016
- self.stream = MockStream.concat_int(self.stream, integer)
- p = parser.GcovStreamParserUtil(self.stream, MAGIC)
- self.assertEqual(p.ReadInt(), integer)
-
- def testReadIntEof(self):
- """Asserts that an error is thrown when the EOF is reached.
- """
- p = parser.GcovStreamParserUtil(self.stream, MAGIC)
- self.assertRaises(parser.FileFormatError, p.ReadInt)
-
- def testReadInt64(self):
- """Asserts that longs are read correctly.
- """
- number = 68719476836
- self.stream = MockStream.concat_int64(self.stream, number)
- p = parser.GcovStreamParserUtil(self.stream, MAGIC)
- self.assertEqual(number, p.ReadInt64())
-
- self.stream = MockStream(format='>')
- self.stream = MockStream.concat_int64(self.stream, number)
- p = parser.GcovStreamParserUtil(self.stream, MAGIC)
- self.assertEqual(number, p.ReadInt64())
-
- def testReadStringNormal(self):
- """Asserts that strings are correctly read from the stream.
-
- Tests the normal case--when the string is correctly formatted.
- """
- test_string = "This is a test."
- self.stream = MockStream.concat_string(self.stream, test_string)
- p = parser.GcovStreamParserUtil(self.stream, MAGIC)
- self.assertEqual(p.ReadString(), test_string)
-
- def testReadStringError(self):
- """Asserts that invalid string format raises error.
-
- Tests when the string length is too short and EOF is reached.
- """
- test_string = "This is a test."
- byte_count = len(test_string)
- word_count = int(round(byte_count / 4.0))
- padding = '\x00' * (4 * word_count - byte_count)
- test_string_padded = test_string + padding
- content = struct.pack('<I', word_count + 1) # will cause EOF error
- content += bytes(test_string_padded)
- self.stream.content += content
- p = parser.GcovStreamParserUtil(self.stream, MAGIC)
- self.assertRaises(parser.FileFormatError, p.ReadString)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/utils/python/coverage/sancov_parser.py b/utils/python/coverage/sancov_parser.py
deleted file mode 100644
index b74f943..0000000
--- a/utils/python/coverage/sancov_parser.py
+++ /dev/null
@@ -1,105 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-"""Parses the contents of a sancov file generated by LLVM Sanitizer Coverage.
-
-
- Typical usage example:
-
- ParseSancovFile(file_name)
-"""
-
-import struct
-import parser
-
-
-MAGIC32 = 0xC0BFFFFFFFFFFF32
-MAGIC64 = 0xC0BFFFFFFFFFFF64
-
-
-class SancovParser(object):
- """Sancov parser object to represent *.sancov files.
-
- Attributes:
- _sancov_file: the stream-like file handle to a sancov file
- _bitness: the bitness of the offsets (32/64)
- _entry_type: the type of each offset (32bit->Integer, 64bit->Long)
- """
-
- def __init__(self, sancov_file):
- """Inits the parser with the input stream and default values.
-
- Args:
- sancov_file: An input binary file stream to a .sancov file
- """
- self._sancov_file = sancov_file
- self._bitness = -1
- self._entry_type = None
- self._size = 0
-
- def Parse(self):
- """Runs the parser to generate the unpacked binary offsets in the file.
-
- Returns:
- A tuple of offsets into the original binary.
-
- Raises:
- parser.FileFormatError: invalid file format or invalid counts.
- """
- self.GetBitness()
- return struct.unpack_from(
- self._entry_type * (self._size * 8 / self._bitness),
- self._sancov_file.read(self._size))
-
- def GetBitness(self):
- """Parses the magic header to determine the bitness.
-
- Returns:
- The sancov file bitness.
-
- Raises:
- parser.FileFormatError: invalid file format or invalid counts.
- """
- if self._bitness > 0:
- return self._bitness
- self._sancov_file.seek(0, 2)
- self._size = self._sancov_file.tell() - 8
- self._sancov_file.seek(0, 0)
- if self._size < 0:
- raise parser.FileFormatError('Empty file.')
- magic = struct.unpack('L', self._sancov_file.read(8))[0];
- if magic == MAGIC64:
- self._entry_type = 'L'
- self._bitness = 64
- elif magic == MAGIC32:
- self._entry_type = 'I'
- self._bitness = 32
- else:
- raise parser.FileFormatError('Invalid magic.')
- return self._bitness
-
-def ParseSancovFile(file_name):
- """Parses the .sancov file specified by the input.
-
- Args:
- file_name: A string file path to a .sancov file
-
- Returns:
- A tuple of bitness, and the unpacked offsets into the original binary.
- """
- with open(file_name, 'rb') as stream:
- p = SancovParser(stream)
- offsets = p.Parse()
- return (p._bitness, offsets)
diff --git a/utils/python/coverage/sancov_parser_test.py b/utils/python/coverage/sancov_parser_test.py
deleted file mode 100644
index 852f2f2..0000000
--- a/utils/python/coverage/sancov_parser_test.py
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2017 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.
-#
-
-import io
-import struct
-import unittest
-import sys
-import os
-
-import parser
-import sancov_parser
-
-
-class SancovParserTest(unittest.TestCase):
- """Tests for sancov_parser in package vts.utils.python.coverage.
-
- Ensures error handling, bitness detection, and correct
- parsing of offsets in the file..
- """
-
- GOLDEN_SANCOV_PATH = os.path.join(sys.path[0], 'testdata/sample.sancov')
- GOLDEN_EXPECTED_BITNESS = 64
- GOLDEN_EXPECTED_OFFSETS = (
- 12115, 12219, 12463, 12527, 17123, 17311, 17507, 17771, 17975, 17987, 18107, 18167,
- 18299, 18503, 18571, 18743, 18755, 18791, 18903, 19127, 19715, 21027, 21123, 21223,
- 21335, 21407, 21455, 21611, 21643, 21683, 23227, 23303, 23343, 23503, 23767, 23779,
- 23791, 23819, 23867, 24615, 24651, 24743, 24775)
-
- def testInvalidMagic(self):
- """Asserts that an exception is raised when the magic is invalid.
- """
- stream = io.BytesIO(struct.pack('L', 0xC0BFFFFFFFFFFF10))
- p = sancov_parser.SancovParser(stream)
- with self.assertRaises(parser.FileFormatError) as context:
- p.Parse()
- self.assertTrue('Invalid magic' in str(context.exception))
-
- def testMagic32(self):
- """Asserts that values are correctly read in 32-bit sancov files.
- """
- stream = io.BytesIO(struct.pack('L', sancov_parser.MAGIC32))
- stream.seek(8)
- values = (1, 2, 3)
- stream.write(struct.pack('III', *values))
- stream.seek(0)
- p = sancov_parser.SancovParser(stream)
- s = p.Parse()
- self.assertEqual(32, p._bitness)
- self.assertEqual(values, s)
-
- def testMagic64(self):
- """Asserts that values are correctly read in 64-bit sancov files.
- """
- stream = io.BytesIO(struct.pack('L', sancov_parser.MAGIC64))
- stream.seek(8)
- values = (4, 5, 6)
- stream.write(struct.pack('LLL', *values))
- stream.seek(0)
- p = sancov_parser.SancovParser(stream)
- s = p.Parse()
- self.assertEqual(64, p._bitness)
- self.assertEqual(values, s)
-
- def testGetBitness32(self):
- """Asserts that bitness is correctly determined from a 32-bit sancov file.
- """
- stream = io.BytesIO(struct.pack('L', sancov_parser.MAGIC32))
- p = sancov_parser.SancovParser(stream)
- self.assertEqual(32, p.GetBitness())
-
- def testGetBitness64(self):
- """Asserts that bitness is correctly determined from a 64-bit sancov file.
- """
- stream = io.BytesIO(struct.pack('L', sancov_parser.MAGIC64))
- p = sancov_parser.SancovParser(stream)
- self.assertEqual(64, p.GetBitness())
-
- def testGolden(self):
- """Asserts that offsets are correctly parsed from the golden file.
- """
- bitness, offsets = sancov_parser.ParseSancovFile(self.GOLDEN_SANCOV_PATH)
- self.assertEqual(self.GOLDEN_EXPECTED_BITNESS, bitness)
- self.assertEqual(self.GOLDEN_EXPECTED_OFFSETS, offsets)
-
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/utils/python/coverage/sancov_utils.py b/utils/python/coverage/sancov_utils.py
deleted file mode 100644
index 17b0954..0000000
--- a/utils/python/coverage/sancov_utils.py
+++ /dev/null
@@ -1,346 +0,0 @@
-#
-# Copyright (C) 2017 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.
-import json
-import logging
-import os
-import shutil
-import subprocess
-import tempfile
-import zipfile
-
-from vts.runners.host import keys
-from vts.utils.python.web import feature_utils
-from vts.utils.python.controllers.adb import AdbError
-from vts.utils.python.coverage import sancov_parser
-
-
-class SancovFeature(feature_utils.Feature):
- """Feature object for sanitizer coverage functionality.
-
- Attributes:
- enabled: boolean, True if sancov is enabled, False otherwise
- web: (optional) WebFeature, object storing web feature util for test run
- """
- _DEFAULT_EXCLUDE_PATHS = [
- 'bionic', 'external/libcxx', 'system/core', 'system/libhidl'
- ]
- _TOGGLE_PARAM = keys.ConfigKeys.IKEY_ENABLE_SANCOV
- _REQUIRED_PARAMS = [keys.ConfigKeys.IKEY_ANDROID_DEVICE]
-
- _PROCESS_INIT_COMMAND = (
- '\"echo coverage=1 > /data/asan/system/asan.options.{0} && '
- 'echo coverage_dir={1}/{2} >> /data/asan/system/asan.options.{0} && '
- 'rm -rf {1}/{2} &&'
- 'mkdir {1}/{2} && '
- 'killall {0}\"')
- _FLUSH_COMMAND = '/data/local/tmp/vts_coverage_configure flush {0}'
- _TARGET_SANCOV_PATH = '/data/misc/trace'
- _SEARCH_PATHS = [(os.path.join('data', 'asan', 'vendor', 'bin'),
- None), (os.path.join('vendor', 'bin'), None),
- (os.path.join('data', 'asan', 'vendor', 'lib'),
- 32), (os.path.join('vendor', 'lib'), 32), (os.path.join(
- 'data', 'asan', 'vendor',
- 'lib64'), 64), (os.path.join('vendor', 'lib64'), 64)]
-
- _BUILD_INFO = 'BUILD_INFO'
- _REPO_DICT = 'repo-dict'
- _SYMBOLS_ZIP = 'symbols.zip'
-
- def __init__(self,
- user_params,
- web=None,
- exclude_paths=_DEFAULT_EXCLUDE_PATHS):
- """Initializes the sanitizer coverage feature.
-
- Args:
- user_params: A dictionary from parameter name (String) to parameter value.
- web: (optional) WebFeature, object storing web feature util for test run.
- exclude_paths: (optional) list of strings, paths to exclude for coverage.
- """
- self.ParseParameters(
- self._TOGGLE_PARAM, self._REQUIRED_PARAMS, user_params=user_params)
- self.web = web
- self._device_resource_dict = {}
- self._file_vectors = {}
- self._exclude_paths = exclude_paths
- if self.enabled:
- android_devices = getattr(self,
- keys.ConfigKeys.IKEY_ANDROID_DEVICE)
- if not isinstance(android_devices, list):
- logging.warn('Android device information not available')
- self.enabled = False
- for device in android_devices:
- serial = str(device.get(keys.ConfigKeys.IKEY_SERIAL))
- sancov_resource_path = str(
- device.get(keys.ConfigKeys.IKEY_SANCOV_RESOURCES_PATH))
- if not serial or not sancov_resource_path:
- logging.warn('Missing sancov information in device: %s',
- device)
- continue
- self._device_resource_dict[serial] = sancov_resource_path
- if self.enabled:
- logging.info('Sancov is enabled.')
- else:
- logging.debug('Sancov is disabled.')
-
- def InitializeDeviceCoverage(self, dut, hals):
- """Initializes the sanitizer coverage on the device for the provided HAL.
-
- Args:
- dut: The device under test.
- hals: A list of the HAL name and version (string) for which to
- measure coverage (e.g. ['android.hardware.light@2.0'])
- """
- serial = dut.adb.shell('getprop ro.serialno').strip()
- if serial not in self._device_resource_dict:
- logging.error("Invalid device provided: %s", serial)
- return
-
- for hal in hals:
- entries = dut.adb.shell(
- 'lshal -itp 2> /dev/null | grep {0}'.format(hal)).splitlines()
- pids = set([
- pid.strip()
- for pid in map(lambda entry: entry.split()[-1], entries)
- if pid.isdigit()
- ])
-
- if len(pids) == 0:
- logging.warn('No matching processes IDs found for HAL %s', hal)
- return
- processes = dut.adb.shell('ps -p {0} -o comm='.format(
- ' '.join(pids))).splitlines()
- process_names = set([
- name.strip() for name in processes
- if name.strip() and not name.endswith(' (deleted)')
- ])
-
- if len(process_names) == 0:
- logging.warn('No matching processes names found for HAL %s',
- hal)
- return
-
- for process_name in process_names:
- cmd = self._PROCESS_INIT_COMMAND.format(
- process_name, self._TARGET_SANCOV_PATH, hal)
- try:
- dut.adb.shell(cmd.format(process_name))
- except AdbError as e:
- logging.error('Command failed: \"%s\"', cmd)
- continue
-
- def FlushDeviceCoverage(self, dut, hals):
- """Flushes the sanitizer coverage on the device for the provided HAL.
-
- Args:
- dut: The device under test.
- hals: A list of HAL name and version (string) for which to flush
- coverage (e.g. ['android.hardware.light@2.0-service'])
- """
- serial = dut.adb.shell('getprop ro.serialno').strip()
- if serial not in self._device_resource_dict:
- logging.error('Invalid device provided: %s', serial)
- return
- for hal in hals:
- dut.adb.shell(self._FLUSH_COMMAND.format(hal))
-
- def _InitializeFileVectors(self, serial, binary_path):
- """Parse the binary and read the debugging information.
-
- Parse the debugging information in the binary to determine executable lines
- of code for all of the files included in the binary.
-
- Args:
- serial: The serial of the device under test.
- binary_path: The path to the unstripped binary on the host.
- """
- file_vectors = self._file_vectors[serial]
- args = ['readelf', '--debug-dump=decodedline', binary_path]
- with tempfile.TemporaryFile('w+b') as tmp:
- subprocess.call(args, stdout=tmp)
- tmp.seek(0)
- file = None
- for entry in tmp:
- entry_parts = entry.split()
- if len(entry_parts) == 0:
- continue
- elif len(entry_parts) < 3 and entry_parts[-1].endswith(':'):
- file = entry_parts[-1].rsplit(':')[0]
- for path in self._exclude_paths:
- if file.startswith(path):
- file = None
- break
- continue
- elif len(entry_parts) == 3 and file is not None:
- line_no_string = entry_parts[1]
- try:
- line = int(line_no_string)
- except ValueError:
- continue
- if file not in file_vectors:
- file_vectors[file] = [-1] * line
- if line > len(file_vectors[file]):
- file_vectors[file].extend(
- [-2] * (line - len(file_vectors[file])))
- file_vectors[file][line - 1] = 0
-
- def _UpdateLineCounts(self, serial, lines):
- """Update the line counts with the symbolized output lines.
-
- Increment the line counts using the symbolized line information.
-
- Args:
- serial: The serial of the device under test.
- lines: A list of strings in the format returned by addr2line (e.g. <file>:<line no>).
- """
- file_vectors = self._file_vectors[serial]
- for line in lines:
- file, line_no_string = line.rsplit(':', 1)
- if file == '??': # some lines cannot be symbolized and will report as '??'
- continue
- try:
- line_no = int(line_no_string)
- except ValueError:
- continue # some lines cannot be symbolized and will report as '??'
- if not file in file_vectors: # file is excluded
- continue
- if line_no > len(file_vectors[file]):
- file_vectors[file].extend([-1] *
- (line_no - len(file_vectors[file])))
- if file_vectors[file][line_no - 1] < 0:
- file_vectors[file][line_no - 1] = 0
- file_vectors[file][line_no - 1] += 1
-
- def Upload(self):
- """Append the coverage information to the web proto report.
- """
- if not self.web or not self.web.enabled:
- return
-
- for device_serial in self._device_resource_dict:
- resource_path = self._device_resource_dict[device_serial]
- rev_map = json.load(
- open(os.path.join(resource_path,
- self._BUILD_INFO)))[self._REPO_DICT]
-
- for file in self._file_vectors[device_serial]:
-
- # Get the git project information
- # Assumes that the project name and path to the project root are similar
- revision = None
- for project_name in rev_map:
- # Matches when source file root and project name are the same
- if file.startswith(str(project_name)):
- git_project_name = str(project_name)
- git_project_path = str(project_name)
- revision = str(rev_map[project_name])
- break
-
- parts = os.path.normpath(str(project_name)).split(
- os.sep, 1)
- # Matches when project name has an additional prefix before the project path root.
- if len(parts) > 1 and file.startswith(parts[-1]):
- git_project_name = str(project_name)
- git_project_path = parts[-1]
- revision = str(rev_map[project_name])
- break
-
- if not revision:
- logging.info("Could not find git info for %s", file)
- continue
-
- covered_count = sum(
- map(lambda count: 1 if count > 0 else 0,
- self._file_vectors[device_serial][file]))
- total_count = sum(
- map(lambda count: 1 if count >= 0 else 0,
- self._file_vectors[device_serial][file]))
- self.web.AddCoverageReport(
- self._file_vectors[device_serial][file], file,
- git_project_name, git_project_path, revision,
- covered_count, total_count, True)
-
- def ProcessDeviceCoverage(self, dut, hals):
- """Process device coverage.
-
- Fetch sancov files from the target, parse the sancov files, symbolize the output,
- and update the line counters.
-
- Args:
- dut: The device under test.
- hals: A list of HAL name and version (string) for which to process
- coverage (e.g. ['android.hardware.light@2.0'])
- """
- serial = dut.adb.shell('getprop ro.serialno').strip()
- product = dut.adb.shell('getprop ro.build.product').strip()
-
- if not serial in self._device_resource_dict:
- logging.error('Invalid device provided: %s', serial)
- return
-
- if serial not in self._file_vectors:
- self._file_vectors[serial] = {}
-
- symbols_zip = zipfile.ZipFile(
- os.path.join(self._device_resource_dict[serial],
- self._SYMBOLS_ZIP))
-
- sancov_files = []
- for hal in hals:
- sancov_files.extend(
- dut.adb.shell('find {0}/{1} -name \"*.sancov\"'.format(
- self._TARGET_SANCOV_PATH, hal)).splitlines())
- temp_dir = tempfile.mkdtemp()
-
- binary_to_sancov = {}
- for file in sancov_files:
- dut.adb.pull(file, temp_dir)
- binary, pid, _ = os.path.basename(file).rsplit('.', 2)
- bitness, offsets = sancov_parser.ParseSancovFile(
- os.path.join(temp_dir, os.path.basename(file)))
- binary_to_sancov[binary] = (bitness, offsets)
-
- for hal in hals:
- dut.adb.shell('rm -rf {0}/{1}'.format(self._TARGET_SANCOV_PATH,
- hal))
-
- search_root = os.path.join('out', 'target', 'product', product,
- 'symbols')
- for path, bitness in self._SEARCH_PATHS:
- for name in [
- f for f in symbols_zip.namelist()
- if f.startswith(os.path.join(search_root, path))
- ]:
- basename = os.path.basename(name)
- if basename in binary_to_sancov and (
- bitness is None
- or binary_to_sancov[basename][0] == bitness):
- with symbols_zip.open(
- name) as source, tempfile.NamedTemporaryFile(
- 'w+b') as target:
- shutil.copyfileobj(source, target)
- target.seek(0)
- self._InitializeFileVectors(serial, target.name)
- addrs = map(lambda addr: '{0:#x}'.format(addr),
- binary_to_sancov[basename][1])
- args = ['addr2line', '-pe', target.name]
- args.extend(addrs)
- with tempfile.TemporaryFile('w+b') as tmp:
- subprocess.call(args, stdout=tmp)
- tmp.seek(0)
- c = tmp.read().split()
- self._UpdateLineCounts(serial, c)
- del binary_to_sancov[basename]
- shutil.rmtree(temp_dir)
diff --git a/utils/python/coverage/testdata/sample.c b/utils/python/coverage/testdata/sample.c
deleted file mode 100644
index 39d245a..0000000
--- a/utils/python/coverage/testdata/sample.c
+++ /dev/null
@@ -1,43 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-
-
-int main(argc, argv)
-int argc;
-char **argv;
-
-{
- int x, y;
- x = 100;
- y = 250;
- testFunctionName(x + y);
-
- printf("The value of x is %d. \n", x);
-
- if (x > y) {
- printf("X is bigger.");
- } else {
- printf("Y is bigger.");
- }
-
- int z = 0;
- int i = 0;
- for (; i < y; i++) {
- z = -z + 2*i;
- }
-
- printf("The value of z is %d.\n", z);
-
- exit(0);
-
-}
-
-int testFunctionName(a)
-int a;
-
-{
- int b;
- b = a << 2;
- printf("%i times 4 = %i\n", a, b);
- return b;
-}
\ No newline at end of file
diff --git a/utils/python/coverage/testdata/sample.gcda b/utils/python/coverage/testdata/sample.gcda
deleted file mode 100644
index 144d91d..0000000
--- a/utils/python/coverage/testdata/sample.gcda
+++ /dev/null
Binary files differ
diff --git a/utils/python/coverage/testdata/sample.gcno b/utils/python/coverage/testdata/sample.gcno
deleted file mode 100644
index 2f99907..0000000
--- a/utils/python/coverage/testdata/sample.gcno
+++ /dev/null
Binary files differ
diff --git a/utils/python/coverage/testdata/sample.sancov b/utils/python/coverage/testdata/sample.sancov
deleted file mode 100644
index b65650b..0000000
--- a/utils/python/coverage/testdata/sample.sancov
+++ /dev/null
Binary files differ