[automerger skipped] Merge "Do not test ELF files that require special program interpreter" into pie-vts-dev
am: 106519795c -s ours
am skip reason: change_id I788aa564d132c3c62692a91ef5be7587a5ed4fae with SHA1 1d61b4b682 is in history
Change-Id: If94f33ae5497fa4d5e5b522adcf6a06b95d66d66
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..749ccda
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
diff --git a/abi/Android.mk b/abi/Android.mk
index 5a34733..98d81af 100644
--- a/abi/Android.mk
+++ b/abi/Android.mk
@@ -19,6 +19,5 @@
include $(CLEAR_VARS)
LOCAL_MODULE := VtsVndkAbi
-VTS_CONFIG_SRC_DIR := testcases/vndk/abi
include test/vts/tools/build/Android.host_config.mk
diff --git a/abi/VtsVndkAbiTest.py b/abi/VtsVndkAbiTest.py
index 34f8ff5..89d12e2 100644
--- a/abi/VtsVndkAbiTest.py
+++ b/abi/VtsVndkAbiTest.py
@@ -15,6 +15,7 @@
# limitations under the License.
#
+import json
import logging
import os
import shutil
@@ -22,34 +23,14 @@
from vts.runners.host import asserts
from vts.runners.host import base_test
-from vts.runners.host import const
from vts.runners.host import keys
from vts.runners.host import test_runner
+from vts.runners.host import utils
from vts.testcases.vndk.golden import vndk_data
-from vts.utils.python.controllers import android_device
+from vts.utils.python.file import target_file_utils
from vts.utils.python.library import elf_parser
-from vts.utils.python.library import vtable_parser
-
-
-def _IterateFiles(root_dir):
- """A generator yielding relative and full paths in a directory.
-
- Args:
- root_dir: The directory to search.
-
- Yields:
- A tuple of (relative_path, full_path) for each regular file.
- relative_path is the relative path to root_dir. full_path is the path
- starting with root_dir.
- """
- for dir_path, dir_names, file_names in os.walk(root_dir):
- if dir_path == root_dir:
- rel_dir = ""
- else:
- rel_dir = os.path.relpath(dir_path, root_dir)
- for file_name in file_names:
- yield (os.path.join(rel_dir, file_name),
- os.path.join(dir_path, file_name))
+from vts.utils.python.library.vtable import vtable_dumper
+from vts.utils.python.vndk import vndk_utils
class VtsVndkAbiTest(base_test.BaseTestClass):
@@ -61,12 +42,6 @@
_vndk_version: String, the VNDK version supported by the device.
data_file_path: The path to VTS data directory.
"""
- _ODM_LIB_DIR_32 = "/odm/lib"
- _ODM_LIB_DIR_64 = "/odm/lib64"
- _VENDOR_LIB_DIR_32 = "/vendor/lib"
- _VENDOR_LIB_DIR_64 = "/vendor/lib64"
- _SYSTEM_LIB_DIR_32 = "/system/lib"
- _SYSTEM_LIB_DIR_64 = "/system/lib64"
def setUpClass(self):
"""Initializes data file path, device, and temporary directory."""
@@ -88,84 +63,170 @@
target_dir: The directory to copy from device.
host_dir: The directory to copy to host.
"""
- test_cmd = "test -d " + target_dir
- logging.info("adb shell %s", test_cmd)
- result = self._dut.adb.shell(test_cmd, no_except=True)
- if result[const.EXIT_CODE]:
+ if not target_file_utils.IsDirectory(target_dir, self._dut.shell):
logging.info("%s doesn't exist. Create %s.", target_dir, host_dir)
- os.mkdir(host_dir, 0750)
+ os.makedirs(host_dir)
return
+ parent_dir = os.path.dirname(host_dir)
+ if parent_dir and not os.path.isdir(parent_dir):
+ os.makedirs(parent_dir)
logging.info("adb pull %s %s", target_dir, host_dir)
self._dut.adb.pull(target_dir, host_dir)
- def _DiffSymbols(self, dump_path, lib_path):
+ def _ToHostPath(self, target_path):
+ """Maps target path to host path in self._temp_dir."""
+ return os.path.join(self._temp_dir, *target_path.strip("/").split("/"))
+
+ @staticmethod
+ def _LoadGlobalSymbolsFromDump(dump_obj):
+ """Loads global symbols from a dump object.
+
+ Args:
+ dump_obj: A dict, the dump in JSON format.
+
+ Returns:
+ A set of strings, the symbol names.
+ """
+ symbols = set()
+ for key in ("elf_functions", "elf_objects"):
+ symbols.update(
+ symbol.get("name", "") for symbol in dump_obj.get(key, []) if
+ symbol.get("binding", "global") == "global")
+ return symbols
+
+ def _DiffElfSymbols(self, dump_obj, parser):
"""Checks if a library includes all symbols in a dump.
Args:
- dump_path: The path to the dump file containing list of symbols.
- lib_path: The path to the library.
+ dump_obj: A dict, the dump in JSON format.
+ parser: An elf_parser.ElfParser that loads the library.
Returns:
A list of strings, the global symbols that are in the dump but not
in the library.
Raises:
- IOError if fails to load the dump.
elf_parser.ElfError if fails to load the library.
"""
- with open(dump_path, "r") as dump_file:
- dump_symbols = set(line.strip() for line in dump_file
- if line.strip())
- parser = elf_parser.ElfParser(lib_path)
- try:
- lib_symbols = parser.ListGlobalDynamicSymbols(include_weak=True)
- finally:
- parser.Close()
+ dump_symbols = self._LoadGlobalSymbolsFromDump(dump_obj)
+ lib_symbols = parser.ListGlobalDynamicSymbols(include_weak=True)
return sorted(dump_symbols.difference(lib_symbols))
- def _DiffVtables(self, dump_path, lib_path):
+ @staticmethod
+ def _DiffVtableComponent(offset, expected_symbol, vtable):
+ """Checks if a symbol is in a vtable entry.
+
+ Args:
+ offset: An integer, the offset of the expected symbol.
+ exepcted_symbol: A string, the name of the expected symbol.
+ vtable: A dict of {offset: [entry]} where offset is an integer and
+ entry is an instance of vtable_dumper.VtableEntry.
+
+ Returns:
+ A list of strings, the actual possible symbols if expected_symbol
+ does not match the vtable entry.
+ None if expected_symbol matches the entry.
+ """
+ if offset not in vtable:
+ return []
+
+ entry = vtable[offset]
+ if not entry.names:
+ return [hex(entry.value).rstrip('L')]
+
+ if expected_symbol not in entry.names:
+ return entry.names
+
+ def _DiffVtableComponents(self, dump_obj, dumper):
"""Checks if a library includes all vtable entries in a dump.
Args:
- dump_path: The path to the dump file containing vtables.
- lib_path: The path to the library.
+ dump_obj: A dict, the dump in JSON format.
+ dumper: An vtable_dumper.VtableDumper that loads the library.
Returns:
- A list of tuples (VTABLE, SYMBOL, EXPECTED_OFFSET, ACTUAL_OFFSET).
- ACTUAL_OFFSET can be "missing" or numbers separated by comma.
+ A list of tuples (VTABLE, OFFSET, EXPECTED_SYMBOL, ACTUAL).
+ ACTUAL can be "missing", a list of symbol names, or an ELF virtual
+ address.
Raises:
- IOError if fails to load the dump.
- vtable_parser.VtableError if fails to load the library.
+ vtable_dumper.VtableError if fails to dump vtable from the library.
"""
- parser = vtable_parser.VtableParser(
- os.path.join(self.data_file_path, "host"))
- with open(dump_path, "r") as dump_file:
- dump_vtables = parser.ParseVtablesFromString(dump_file.read())
+ function_kinds = [
+ "function_pointer",
+ "complete_dtor_pointer",
+ "deleting_dtor_pointer"
+ ]
+ non_function_kinds = [
+ "vcall_offset",
+ "vbase_offset",
+ "offset_to_top",
+ "rtti",
+ "unused_function_pointer"
+ ]
+ default_vtable_component_kind = "function_pointer"
- lib_vtables = parser.ParseVtablesFromLibrary(lib_path)
- # TODO(b/78316564): The dumper doesn't support SHT_ANDROID_RELA.
- if not lib_vtables and self.run_as_compliance_test:
- logging.warning("%s: Cannot dump vtables",
- os.path.relpath(lib_path, self._temp_dir))
- return []
- logging.debug("%s: %s", lib_path, lib_vtables)
- diff = []
- for vtable, dump_symbols in dump_vtables.iteritems():
- lib_inv_vtable = dict()
- if vtable in lib_vtables:
- for off, sym in lib_vtables[vtable]:
- if sym not in lib_inv_vtable:
- lib_inv_vtable[sym] = [off]
- else:
- lib_inv_vtable[sym].append(off)
- for off, sym in dump_symbols:
- if sym not in lib_inv_vtable:
- diff.append((vtable, sym, str(off), "missing"))
- elif off not in lib_inv_vtable[sym]:
- diff.append((vtable, sym, str(off),
- ",".join(str(x) for x in lib_inv_vtable[sym])))
- return diff
+ global_symbols = self._LoadGlobalSymbolsFromDump(dump_obj)
+
+ lib_vtables = {vtable.name: vtable
+ for vtable in dumper.DumpVtables()}
+ logging.debug("\n\n".join(str(vtable)
+ for _, vtable in lib_vtables.iteritems()))
+
+ vtables_diff = []
+ for record_type in dump_obj.get("record_types", []):
+ type_name_symbol = record_type.get("unique_id", "")
+ vtable_symbol = type_name_symbol.replace("_ZTS", "_ZTV", 1)
+
+ # Skip if the vtable symbol isn't global.
+ if vtable_symbol not in global_symbols:
+ continue
+
+ # Collect vtable entries from library dump.
+ if vtable_symbol in lib_vtables:
+ lib_vtable = {entry.offset: entry
+ for entry in lib_vtables[vtable_symbol].entries}
+ else:
+ lib_vtable = dict()
+
+ for index, entry in enumerate(record_type.get("vtable_components",
+ [])):
+ entry_offset = index * int(self.abi_bitness) // 8
+ entry_kind = entry.get("kind", default_vtable_component_kind)
+ entry_symbol = entry.get("mangled_component_name", "")
+ entry_is_pure = entry.get("is_pure", False)
+
+ if entry_kind in non_function_kinds:
+ continue
+
+ if entry_kind not in function_kinds:
+ logging.warning("%s: Unexpected vtable entry kind %s",
+ vtable_symbol, entry_kind)
+
+ if entry_symbol not in global_symbols:
+ # Itanium cxx abi doesn't specify pure virtual vtable
+ # entry's behaviour. However we can still do some checks
+ # based on compiler behaviour.
+ # Even though we don't check weak symbols, we can still
+ # issue a warning when a pure virtual function pointer
+ # is missing.
+ if entry_is_pure and entry_offset not in lib_vtable:
+ logging.warning("%s: Expected pure virtual function"
+ "in %s offset %s",
+ vtable_symbol, vtable_symbol,
+ entry_offset)
+ continue
+
+ diff_symbols = self._DiffVtableComponent(
+ entry_offset, entry_symbol, lib_vtable)
+ if diff_symbols is None:
+ continue
+
+ vtables_diff.append(
+ (vtable_symbol, str(entry_offset), entry_symbol,
+ (",".join(diff_symbols) if diff_symbols else "missing")))
+
+ return vtables_diff
def _ScanLibDirs(self, dump_dir, lib_dirs, dump_version):
"""Compares dump files with libraries copied from device.
@@ -182,74 +243,70 @@
An integer, number of incompatible libraries.
"""
error_count = 0
- symbol_dumps = dict()
- vtable_dumps = dict()
+ dump_paths = dict()
lib_paths = dict()
- for dump_rel_path, dump_path in _IterateFiles(dump_dir):
- if dump_rel_path.endswith("_symbol.dump"):
- lib_name = dump_rel_path.rpartition("_symbol.dump")[0]
- symbol_dumps[lib_name] = dump_path
- elif dump_rel_path.endswith("_vtable.dump"):
- lib_name = dump_rel_path.rpartition("_vtable.dump")[0]
- vtable_dumps[lib_name] = dump_path
+ for parent_dir, dump_name in utils.iterate_files(dump_dir):
+ dump_path = os.path.join(parent_dir, dump_name)
+ if dump_path.endswith(".dump"):
+ lib_name = dump_name.rpartition(".dump")[0]
+ dump_paths[lib_name] = dump_path
else:
logging.warning("Unknown dump: %s", dump_path)
- continue
- lib_paths[lib_name] = None
for lib_dir in lib_dirs:
- for lib_rel_path, lib_path in _IterateFiles(lib_dir):
- try:
- vndk_dir = next(x for x in ("vndk", "vndk-sp") if
- lib_rel_path.startswith(x + os.path.sep))
- lib_name = lib_rel_path.replace(
- vndk_dir, vndk_dir + "-" + dump_version, 1)
- except StopIteration:
- lib_name = lib_rel_path
+ for parent_dir, lib_name in utils.iterate_files(lib_dir):
+ if lib_name not in lib_paths:
+ lib_paths[lib_name] = os.path.join(parent_dir, lib_name)
- if lib_name in lib_paths and not lib_paths[lib_name]:
- lib_paths[lib_name] = lib_path
-
- for lib_name, lib_path in lib_paths.iteritems():
- if not lib_path:
+ for lib_name, dump_path in dump_paths.iteritems():
+ if lib_name not in lib_paths:
logging.info("%s: Not found on target", lib_name)
continue
+ lib_path = lib_paths[lib_name]
rel_path = os.path.relpath(lib_path, self._temp_dir)
has_exception = False
missing_symbols = []
vtable_diff = []
- # Compare symbols
- if lib_name in symbol_dumps:
- try:
- missing_symbols = self._DiffSymbols(
- symbol_dumps[lib_name], lib_path)
- except (IOError, elf_parser.ElfError):
- logging.exception("%s: Cannot diff symbols", rel_path)
- has_exception = True
- # Compare vtables
- if lib_name in vtable_dumps:
- try:
- vtable_diff = self._DiffVtables(
- vtable_dumps[lib_name], lib_path)
- except (IOError, vtable_parser.VtableError):
- logging.exception("%s: Cannot diff vtables", rel_path)
- has_exception = True
+
+ try:
+ with open(dump_path, "r") as dump_file:
+ dump_obj = json.load(dump_file)
+ with vtable_dumper.VtableDumper(lib_path) as dumper:
+ missing_symbols = self._DiffElfSymbols(
+ dump_obj, dumper)
+ vtable_diff = self._DiffVtableComponents(
+ dump_obj, dumper)
+ except (IOError,
+ elf_parser.ElfError,
+ vtable_dumper.VtableError) as e:
+ logging.exception("%s: Cannot diff ABI", rel_path)
+ has_exception = True
if missing_symbols:
logging.error("%s: Missing Symbols:\n%s",
rel_path, "\n".join(missing_symbols))
if vtable_diff:
logging.error("%s: Vtable Difference:\n"
- "vtable symbol expected actual\n%s",
+ "vtable offset expected actual\n%s",
rel_path,
- "\n".join(" ".join(x) for x in vtable_diff))
- if has_exception or missing_symbols or vtable_diff:
+ "\n".join(" ".join(e) for e in vtable_diff))
+ if (has_exception or missing_symbols or vtable_diff):
error_count += 1
else:
logging.info("%s: Pass", rel_path)
return error_count
+ @staticmethod
+ def _GetLinkerSearchIndex(target_path):
+ """Returns the key for sorting linker search paths."""
+ index = 0
+ for prefix in ("/odm", "/vendor", "/system"):
+ if target_path.startswith(prefix):
+ return index
+ index += 1
+ return index
+
def testAbiCompatibility(self):
"""Checks ABI compliance of VNDK libraries."""
primary_abi = self._dut.getCpuAbiList()[0]
@@ -273,26 +330,20 @@
self._vndk_version, primary_abi, self.abi_bitness))
logging.info("dump dir: %s", dump_dir)
- odm_lib_dir = os.path.join(
- self._temp_dir, "odm_lib_dir_" + self.abi_bitness)
- vendor_lib_dir = os.path.join(
- self._temp_dir, "vendor_lib_dir_" + self.abi_bitness)
- system_lib_dir = os.path.join(
- self._temp_dir, "system_lib_dir_" + self.abi_bitness)
- logging.info("host lib dir: %s %s %s",
- odm_lib_dir, vendor_lib_dir, system_lib_dir)
- self._PullOrCreateDir(
- getattr(self, "_ODM_LIB_DIR_" + self.abi_bitness),
- odm_lib_dir)
- self._PullOrCreateDir(
- getattr(self, "_VENDOR_LIB_DIR_" + self.abi_bitness),
- vendor_lib_dir)
- self._PullOrCreateDir(
- getattr(self, "_SYSTEM_LIB_DIR_" + self.abi_bitness),
- system_lib_dir)
+ target_vndk_dir = vndk_utils.GetVndkCoreDirectory(self.abi_bitness,
+ self._vndk_version)
+ target_vndk_sp_dir = vndk_utils.GetVndkSpDirectory(self.abi_bitness,
+ self._vndk_version)
+ target_dirs = vndk_utils.GetVndkExtDirectories(self.abi_bitness)
+ target_dirs += vndk_utils.GetVndkSpExtDirectories(self.abi_bitness)
+ target_dirs += [target_vndk_dir, target_vndk_sp_dir]
+ target_dirs.sort(key=self._GetLinkerSearchIndex)
- error_count = self._ScanLibDirs(
- dump_dir, [odm_lib_dir, vendor_lib_dir, system_lib_dir], dump_version)
+ host_dirs = [self._ToHostPath(x) for x in target_dirs]
+ for target_dir, host_dir in zip(target_dirs, host_dirs):
+ self._PullOrCreateDir(target_dir, host_dir)
+
+ error_count = self._ScanLibDirs(dump_dir, host_dirs, dump_version)
asserts.assertEqual(error_count, 0,
"Total number of errors: " + str(error_count))
diff --git a/dependency/Android.mk b/dependency/Android.mk
index e527023..2c538db 100644
--- a/dependency/Android.mk
+++ b/dependency/Android.mk
@@ -21,7 +21,5 @@
include $(CLEAR_VARS)
LOCAL_MODULE := VtsVndkDependency
-
-VTS_CONFIG_SRC_DIR := testcases/vndk/dependency
include test/vts/tools/build/Android.host_config.mk
diff --git a/dependency/VtsVndkDependencyTest.py b/dependency/VtsVndkDependencyTest.py
index 094d84a..4878978 100644
--- a/dependency/VtsVndkDependencyTest.py
+++ b/dependency/VtsVndkDependencyTest.py
@@ -111,11 +111,7 @@
sp_hal_strings = vndk_lists[0]
self._sp_hal = [re.compile(x) for x in sp_hal_strings]
- (self._ll_ndk,
- self._vndk,
- self._vndk_sp) = (
- set(path_utils.TargetBaseName(path) for path in vndk_list)
- for vndk_list in vndk_lists[1:])
+ (self._ll_ndk, self._vndk, self._vndk_sp) = vndk_lists[1:]
logging.debug("LL_NDK: %s", self._ll_ndk)
logging.debug("SP_HAL: %s", sp_hal_strings)
diff --git a/files/VtsVndkFilesTest.py b/files/VtsVndkFilesTest.py
index 5f08ccf..d88cd8a 100644
--- a/files/VtsVndkFilesTest.py
+++ b/files/VtsVndkFilesTest.py
@@ -68,20 +68,21 @@
return target_file_utils.FindFiles(self._shell, dir_path, "*",
"! -type d")
- def _TestVndkDirectory(self, vndk_dir, *vndk_list_names):
+ def _TestVndkDirectory(self, vndk_dir, vndk_list_names):
"""Verifies that the VNDK directory doesn't contain extra files.
Args:
vndk_dir: The path to the VNDK directory on device.
- *vndk_list_names: Strings, the categories of the VNDK libraries
- that can be in the directory.
+ vndk_list_names: Strings, the categories of the VNDK libraries
+ that can be in the directory.
"""
vndk_lists = vndk_data.LoadVndkLibraryLists(
self.data_file_path, self._vndk_version, *vndk_list_names)
asserts.assertTrue(vndk_lists, "Cannot load VNDK library lists.")
vndk_set = set().union(*vndk_lists)
logging.debug("vndk set: %s", vndk_set)
- unexpected = set(self._ListFiles(vndk_dir)) - vndk_set
+ unexpected = [x for x in self._ListFiles(vndk_dir) if
+ path_utils.TargetBaseName(x) not in vndk_set]
if unexpected:
logging.error("Unexpected files:\n%s", "\n".join(unexpected))
asserts.fail("Total number of errors: %d" % len(unexpected))
@@ -99,9 +100,7 @@
vndk_lists = vndk_data.LoadVndkLibraryLists(
self.data_file_path, self._vndk_version, *vndk_list_names)
asserts.assertTrue(vndk_lists, "Cannot load VNDK library lists.")
- vndk_set = set()
- for vndk_list in vndk_lists:
- vndk_set.update(path_utils.TargetBaseName(x) for x in vndk_list)
+ vndk_set = set().union(*vndk_lists)
vndk_set.difference_update(except_libs)
logging.debug("vndk set: %s", vndk_set)
unexpected = [x for x in self._ListFiles(vndk_dir) if
@@ -117,16 +116,14 @@
self._TestVndkDirectory(
vndk_utils.GetVndkCoreDirectory(
self.abi_bitness, self._vndk_version),
- vndk_data.VNDK,
- vndk_data.VNDK_PRIVATE)
+ (vndk_data.VNDK, vndk_data.VNDK_PRIVATE,))
def testVndkSpDirectory(self):
"""Verifies that VNDK-SP directory doesn't contain extra files."""
self._TestVndkDirectory(
vndk_utils.GetVndkSpDirectory(
self.abi_bitness, self._vndk_version),
- vndk_data.VNDK_SP,
- vndk_data.VNDK_SP_PRIVATE)
+ (vndk_data.VNDK_SP, vndk_data.VNDK_SP_PRIVATE,))
def testNoLlndkInVendor(self):
"""Verifies that vendor partition has no LL-NDK libraries."""
diff --git a/golden/Android.bp b/golden/Android.bp
new file mode 100644
index 0000000..20222e1
--- /dev/null
+++ b/golden/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2018 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.
+
+python_binary_host {
+ name: "extract_lsdump",
+ main: "extract_lsdump.py",
+ srcs: ["extract_lsdump.py"],
+ version: {
+ py2: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ }
+}
diff --git a/golden/dump_abi.py b/golden/dump_abi.py
index 71f9f00..a5767ae 100755
--- a/golden/dump_abi.py
+++ b/golden/dump_abi.py
@@ -16,52 +16,11 @@
#
import argparse
-import csv
-import importlib
import os
import subprocess
import sys
-
-class ExternalModules(object):
- """This class imports modules dynamically and keeps them as attributes.
-
- Assume the user runs this script in the source directory. The VTS modules
- are outside the search path and thus have to be imported dynamically.
-
- Attribtues:
- ar_parser: The ar_parser module.
- elf_parser: The elf_parser module.
- vtable_parser: The vtable_parser module.
- """
- @classmethod
- def ImportParsers(cls, import_dir):
- """Imports elf_parser and vtable_parser.
-
- Args:
- import_dir: The directory containing vts.utils.python.library.*.
- """
- sys.path.append(import_dir)
- cls.ar_parser = importlib.import_module(
- "vts.utils.python.library.ar_parser")
- cls.elf_parser = importlib.import_module(
- "vts.utils.python.library.elf_parser")
- cls.vtable_parser = importlib.import_module(
- "vts.utils.python.library.vtable_parser")
-
-
-def _CreateAndWrite(path, data):
- """Creates directories on a file path and writes data to it.
-
- Args:
- path: The path to the file.
- data: The data to write.
- """
- dir_name = os.path.dirname(path)
- if dir_name and not os.path.exists(dir_name):
- os.makedirs(dir_name)
- with open(path, "w") as f:
- f.write(data)
+import extract_lsdump
def _ExecuteCommand(cmd, **kwargs):
@@ -103,130 +62,43 @@
return [line.split("=", 1)[1].strip("'") for line in stdout.splitlines()]
-def FindBinary(file_name):
- """Finds an executable binary in environment variable PATH.
+def _LoadLibraryNamesFromTxt(vndk_lib_list_file):
+ """Loads VNDK and VNDK-SP library names from a VNDK library list.
Args:
- file_name: The file name to find.
-
- Returns:
- A string which is the path to the binary.
- """
- return _ExecuteCommand(["which", file_name])
-
-
-def DumpSymbols(lib_path, dump_path, exclude_symbols):
- """Dump symbols from a library to a dump file.
-
- The dump file is a sorted list of symbols. Each line contains one symbol.
-
- Args:
- lib_path: The path to the library.
- dump_path: The path to the dump file.
- exclude_symbols: A set of strings, the symbols that should not be
- written to the dump file.
-
- Returns:
- A list of strings which are the symbols written to the dump file.
-
- Raises:
- elf_parser.ElfError if fails to load the library.
- IOError if fails to write to the dump.
- """
- elf_parser = ExternalModules.elf_parser
- parser = None
- try:
- parser = elf_parser.ElfParser(lib_path)
- symbols = [x for x in parser.ListGlobalDynamicSymbols()
- if x not in exclude_symbols]
- finally:
- if parser:
- parser.Close()
- if symbols:
- symbols.sort()
- _CreateAndWrite(dump_path, "\n".join(symbols) + "\n")
- return symbols
-
-
-def DumpVtables(lib_path, dump_path, dumper_dir, include_symbols):
- """Dump vtables from a library to a dump file.
-
- The dump file is the raw output of vndk-vtable-dumper.
-
- Args:
- lib_path: The path to the library.
- dump_path: The path to the text file.
- dumper_dir: The path to the directory containing the dumper executable
- and library.
- include_symbols: A set of strings. A vtable is written to the dump file
- only if its symbol is in the set.
-
- Returns:
- A string which is the content written to the dump file.
-
- Raises:
- vtable_parser.VtableError if fails to load the library.
- IOError if fails to write to the dump.
- """
- vtable_parser = ExternalModules.vtable_parser
- parser = vtable_parser.VtableParser(dumper_dir)
-
- def GenerateLines():
- for line in parser.CallVtableDumper(lib_path).split("\n"):
- parsed_lines.append(line)
- yield line
-
- lines = GenerateLines()
- dump_lines = []
- try:
- while True:
- parsed_lines = []
- vtable, entries = parser.ParseOneVtable(lines)
- if vtable in include_symbols:
- dump_lines.extend(parsed_lines)
- except StopIteration:
- pass
-
- dump_string = "\n".join(dump_lines).strip("\n")
- if dump_string:
- dump_string += "\n"
- _CreateAndWrite(dump_path, dump_string)
- return dump_string
-
-
-def _LoadLibraryNamesFromCsv(csv_file):
- """Loads VNDK and VNDK-SP library names from an eligible list.
-
- Args:
- csv_file: A file object of eligible-list.csv.
+ vndk_lib_list_file: A file object of
+ build/make/target/product/vndk/current.txt
Returns:
A list of strings, the VNDK and VNDK-SP library names with vndk/vndk-sp
directory prefixes.
"""
- lib_names = []
- # Skip header
- next(csv_file)
- reader = csv.reader(csv_file)
- for cells in reader:
- if cells[1] not in ("VNDK", "VNDK-SP"):
- continue
- lib_name = os.path.normpath(cells[0]).replace("/system/${LIB}/", "", 1)
- if not (lib_name.startswith("vndk") or lib_name.startswith("vndk-sp")):
- continue
- lib_name = lib_name.replace("${VNDK_VER}", "{VNDK_VER}")
- lib_names.append(lib_name)
- return lib_names
+ tags = (
+ ("VNDK-core: ", len("VNDK-core: "), False),
+ ("VNDK-SP: ", len("VNDK-SP: "), False),
+ ("VNDK-private: ", len("VNDK-private: "), True),
+ ("VNDK-SP-private: ", len("VNDK-SP-private: "), True),
+ )
+ lib_names = set()
+ lib_names_exclude = set()
+ for line in vndk_lib_list_file:
+ for tag, tag_len, is_exclude in tags:
+ if line.startswith(tag):
+ lib_name = line[tag_len:].strip()
+ if is_exclude:
+ lib_names_exclude.add(lib_name)
+ else:
+ lib_names.add(lib_name)
+ return sorted(lib_names - lib_names_exclude)
def _LoadLibraryNames(file_names):
"""Loads library names from files.
- Each element in the input list can be a .so file, a text file, or a .csv
- file. The returned list consists of:
+ Each element in the input list can be a .so file or a .txt file. The
+ returned list consists of:
- The .so file names in the input list.
- - The non-empty lines in the text files.
- - The libraries tagged with VNDK or VNDK-SP in the CSV.
+ - The libraries tagged with VNDK-core or VNDK-SP in the .txt file.
Args:
file_names: A list of strings, the library or text file names.
@@ -239,143 +111,197 @@
for file_name in file_names:
if file_name.endswith(".so"):
lib_names.append(file_name)
- elif file_name.endswith(".csv"):
- with open(file_name, "r") as csv_file:
- lib_names.extend(_LoadLibraryNamesFromCsv(csv_file))
else:
- with open(file_name, "r") as lib_list:
- lib_names.extend(line.strip() for line in lib_list
- if line.strip())
+ with open(file_name, "r") as txt_file:
+ lib_names.extend(_LoadLibraryNamesFromTxt(txt_file))
return lib_names
-def DumpAbi(output_dir, lib_names, lib_dir, object_dir, dumper_dir):
- """Generates dump from libraries.
+def DumpAbi(output_dir, lib_names, lsdump_path):
+ """Generates ABI dumps from library lsdumps.
Args:
output_dir: The output directory of dump files.
lib_names: The names of the libraries to dump.
- lib_dir: The path to the directory containing the libraries to dump.
- object_dir: The path to the directory containing intermediate objects.
- dumper_dir: The path to the directory containing the vtable dumper
- executable and library.
+ lsdump_path: The path to the directory containing lsdumps.
Returns:
- A list of strings, the paths to the libraries not found in lib_dir.
+ A list of strings, the libraries whose ABI dump fails to be created.
"""
- ar_parser = ExternalModules.ar_parser
- static_symbols = set()
- for ar_name in ("libgcc", "libatomic", "libcompiler_rt-extras"):
- ar_path = os.path.join(
- object_dir, "STATIC_LIBRARIES", ar_name + "_intermediates",
- ar_name + ".a")
- static_symbols.update(ar_parser.ListGlobalSymbols(ar_path))
-
- missing_libs = []
- dump_dir = os.path.join(output_dir, os.path.basename(lib_dir))
+ missing_dumps = []
for lib_name in lib_names:
- lib_path = os.path.join(lib_dir, lib_name)
- symbol_dump_path = os.path.join(dump_dir, lib_name + "_symbol.dump")
- vtable_dump_path = os.path.join(dump_dir, lib_name + "_vtable.dump")
- print(lib_path)
- if not os.path.isfile(lib_path):
- missing_libs.append(lib_path)
- print("Warning: Not found")
- print("")
- continue
- symbols = DumpSymbols(lib_path, symbol_dump_path, static_symbols)
- if symbols:
- print("Output: " + symbol_dump_path)
+ dump_path = os.path.join(output_dir, lib_name + '.abi.dump')
+ lib_lsdump_path = os.path.join(lsdump_path, lib_name + '.lsdump')
+ if os.path.isfile(lib_lsdump_path + '.gz'):
+ lib_lsdump_path += '.gz'
+
+ print(lib_lsdump_path)
+ try:
+ extract_lsdump.ParseLsdumpFile(lib_lsdump_path, dump_path)
+ except extract_lsdump.LsdumpError as e:
+ missing_dumps.append(lib_name)
+ print(e)
else:
- print("No symbols")
- vtables = DumpVtables(
- lib_path, vtable_dump_path, dumper_dir, set(symbols))
- if vtables:
- print("Output: " + vtable_dump_path)
- else:
- print("No vtables")
- print("")
- return missing_libs
+ print('Output: ' + dump_path)
+ print('')
+ return missing_dumps
+
+
+def _GetTargetArchDir(target_arch, target_arch_variant):
+ if target_arch == target_arch_variant:
+ return target_arch
+ return '{}_{}'.format(target_arch, target_arch_variant)
+
+
+def _GetAbiBitnessFromArch(target_arch):
+ arch_bitness = {
+ 'arm': '32',
+ 'arm64': '64',
+ 'x86': '32',
+ 'x86_64': '64',
+ }
+ return arch_bitness[target_arch]
def main():
# Parse arguments
- arg_parser = argparse.ArgumentParser()
+ description = (
+ 'Generates VTS VNDK ABI test abidumps from lsdump. '
+ 'Option values are read from build variables if no value is given. '
+ 'If none of the options are specified, then abidumps for target second '
+ 'arch are also generated.'
+ )
+ arg_parser = argparse.ArgumentParser(description=description)
arg_parser.add_argument("file", nargs="*",
help="the libraries to dump. Each file can be "
- ".so, text, or .csv. The text file contains "
- "a list of paths. The CSV file follows the"
- "format of eligible list output from VNDK"
- "definition tool. {VNDK_VER} in the library"
- "paths is replaced with "
- "\"-\" + PLATFORM_VNDK_VERSION.")
- arg_parser.add_argument("--dumper-dir", "-d", action="store",
- help="the path to the directory containing "
- "bin/vndk-vtable-dumper.")
- arg_parser.add_argument("--import-path", "-i", action="store",
- help="the directory for VTS python modules. "
- "Default value is $ANDROID_BUILD_TOP/test")
+ ".so or .txt. The text file can be found at "
+ "build/make/target/product/vndk/current.txt.")
arg_parser.add_argument("--output", "-o", action="store",
help="output directory for ABI reference dump. "
"Default value is PLATFORM_VNDK_VERSION.")
+ arg_parser.add_argument('--platform-vndk-version',
+ help='platform VNDK version. '
+ 'Default value is PLATFORM_VNDK_VERSION.')
+ arg_parser.add_argument('--binder-bitness',
+ choices=['32', '64'],
+ help='bitness of binder interface. '
+ 'Default value is 32 if BINDER32BIT is set '
+ 'else is 64.')
+ arg_parser.add_argument('--target-main-arch',
+ choices=['arm', 'arm64', 'x86', 'x86_64'],
+ help='main CPU arch of the device. '
+ 'Default value is TARGET_ARCH.')
+ arg_parser.add_argument('--target-arch',
+ choices=['arm', 'arm64', 'x86', 'x86_64'],
+ help='CPU arch of the libraries to dump. '
+ 'Default value is TARGET_ARCH.')
+ arg_parser.add_argument('--target-arch-variant',
+ help='CPU arch variant of the libraries to dump. '
+ 'Default value is TARGET_ARCH_VARIANT.')
+
args = arg_parser.parse_args()
- # Get target architectures
build_top_dir = os.getenv("ANDROID_BUILD_TOP")
if not build_top_dir:
sys.exit("env var ANDROID_BUILD_TOP is not set")
- (binder_32_bit,
- vndk_version,
- target_arch,
- target_2nd_arch) = GetBuildVariables(
- build_top_dir, abs_path=False, vars=(
- "BINDER32BIT",
- "PLATFORM_VNDK_VERSION",
- "TARGET_ARCH",
- "TARGET_2ND_ARCH"))
+ # If some options are not specified, read build variables as default values.
+ if not all([args.platform_vndk_version,
+ args.binder_bitness,
+ args.target_main_arch,
+ args.target_arch,
+ args.target_arch_variant]):
+ [platform_vndk_version,
+ binder_32_bit,
+ target_arch,
+ target_arch_variant,
+ target_2nd_arch,
+ target_2nd_arch_variant] = GetBuildVariables(
+ build_top_dir,
+ False,
+ ['PLATFORM_VNDK_VERSION',
+ 'BINDER32BIT',
+ 'TARGET_ARCH',
+ 'TARGET_ARCH_VARIANT',
+ 'TARGET_2ND_ARCH',
+ 'TARGET_2ND_ARCH_VARIANT']
+ )
+ target_main_arch = target_arch
+ binder_bitness = '32' if binder_32_bit else '64'
- (target_lib_dir,
- target_obj_dir,
- target_2nd_lib_dir,
- target_2nd_obj_dir) = GetBuildVariables(
- build_top_dir, abs_path=True, vars=(
- "TARGET_OUT_SHARED_LIBRARIES",
- "TARGET_OUT_INTERMEDIATES",
- "2ND_TARGET_OUT_SHARED_LIBRARIES",
- "2ND_TARGET_OUT_INTERMEDIATES"))
+ if args.platform_vndk_version:
+ platform_vndk_version = args.platform_vndk_version
- # Import elf_parser and vtable_parser
- ExternalModules.ImportParsers(args.import_path if args.import_path else
- os.path.join(build_top_dir, "test"))
+ if args.binder_bitness:
+ binder_bitness = args.binder_bitness
- # Find vtable dumper
- if args.dumper_dir:
- dumper_dir = args.dumper_dir
- else:
- dumper_path = FindBinary(
- ExternalModules.vtable_parser.VtableParser.VNDK_VTABLE_DUMPER)
- dumper_dir = os.path.dirname(os.path.dirname(dumper_path))
- print("DUMPER_DIR=" + dumper_dir)
+ if args.target_main_arch:
+ target_main_arch = args.target_main_arch
- output_dir = os.path.join((args.output if args.output else vndk_version),
- ("binder32" if binder_32_bit else "binder64"),
- target_arch)
- print("OUTPUT_DIR=" + output_dir)
+ if args.target_arch:
+ target_arch = args.target_arch
- lib_names = [name.format(VNDK_VER="-" + vndk_version) for
- name in _LoadLibraryNames(args.file)]
+ if args.target_arch_variant:
+ target_arch_variant = args.target_arch_variant
- missing_libs = DumpAbi(output_dir, lib_names, target_lib_dir,
- target_obj_dir, dumper_dir)
- if target_2nd_arch:
- missing_libs += DumpAbi(output_dir, lib_names, target_2nd_lib_dir,
- target_2nd_obj_dir, dumper_dir)
+ dump_targets = [(platform_vndk_version,
+ binder_bitness,
+ target_main_arch,
+ target_arch,
+ target_arch_variant)]
- if missing_libs:
- print("Warning: Could not find libraries:")
- for lib_path in missing_libs:
- print(lib_path)
+ # If all options are not specified, then also create dump for 2nd arch.
+ if not any([args.platform_vndk_version,
+ args.binder_bitness,
+ args.target_main_arch,
+ args.target_arch,
+ args.target_arch_variant]):
+ dump_targets.append((platform_vndk_version,
+ binder_bitness,
+ target_main_arch,
+ target_2nd_arch,
+ target_2nd_arch_variant))
+
+ for target_tuple in dump_targets:
+ (platform_vndk_version,
+ binder_bitness,
+ target_main_arch,
+ target_arch,
+ target_arch_variant) = target_tuple
+
+ # Determine abi_bitness from target architecture
+ abi_bitness = _GetAbiBitnessFromArch(target_arch)
+
+ # Generate ABI dump from lsdump in TOP/prebuilts/abi-dumps
+ lsdump_path = os.path.join(
+ build_top_dir,
+ 'prebuilts',
+ 'abi-dumps',
+ 'vndk',
+ platform_vndk_version,
+ binder_bitness,
+ _GetTargetArchDir(target_arch, target_arch_variant),
+ 'source-based')
+ if not os.path.exists(lsdump_path):
+ print('Warning: lsdump path does not exist: ' + lsdump_path)
+ print('No abidump created.')
+ continue
+
+ output_dir = os.path.join(
+ args.output if args.output else platform_vndk_version,
+ 'binder' + binder_bitness,
+ target_main_arch,
+ 'lib64' if abi_bitness == '64' else 'lib')
+ print("OUTPUT_DIR=" + output_dir)
+
+ lib_names = _LoadLibraryNames(args.file)
+
+ missing_dumps = DumpAbi(output_dir, lib_names, lsdump_path)
+
+ if missing_dumps:
+ print('Warning: Fails to create ABI dumps for libraries:')
+ for lib_name in missing_dumps:
+ print(lib_name)
if __name__ == "__main__":
diff --git a/golden/extract_lsdump.py b/golden/extract_lsdump.py
new file mode 100755
index 0000000..45e9c38
--- /dev/null
+++ b/golden/extract_lsdump.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 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 gzip
+import json
+import os
+
+
+class LsdumpError(Exception):
+ """The exception raised by ParseLsdumpFile."""
+ pass
+
+
+class AttrDict(dict):
+ """A dictionary with attribute accessors."""
+
+ def __getattr__(self, key):
+ """Returns self[key]."""
+ try:
+ return self[key]
+ except KeyError:
+ raise AttributeError('Failed to get attribute: %s' % key)
+
+ def __setattr__(self, key, value):
+ """Assigns value to self[key]."""
+ self[key] = value
+
+
+def _OpenFileOrGzipped(file_name):
+ """Opens a file that is either in gzip or uncompressed format.
+
+ If file_name ends with '.gz' then return gzip.open(file_name, 'rb'),
+ else return open(file_name, 'rb').
+
+ Args:
+ file_name: The file name to open.
+
+ Returns:
+ A file object.
+
+ Raises:
+ IOError if fails to open.
+ """
+ if file_name.endswith('.gz'):
+ return gzip.open(file_name, 'rb')
+ return open(file_name, 'rb')
+
+
+def _ConsumeOffset(tok, beg=0):
+ """Consumes a <offset-number> in a thunk symbol."""
+ pos = tok.find('_', beg) + 1
+ return tok[:pos], tok[pos:]
+
+
+def _ConsumeCallOffset(tok):
+ """Consumes a <call-offset> in a thunk symbol."""
+ if tok[:1] == 'h':
+ lhs, rhs = _ConsumeOffset(tok, 1)
+ elif tok[:1] == 'v':
+ lhs, rhs = _ConsumeOffset(tok, 1)
+ lhs2, rhs = _ConsumeOffset(rhs)
+ if lhs and lhs2:
+ lhs = lhs + lhs2
+ else:
+ lhs, rhs = '', tok
+ else:
+ lhs, rhs = '', tok
+ return lhs, rhs
+
+
+def _FindThunkTarget(name):
+ """Finds thunk symbol's target function.
+
+ <thunk-symbol> ::= _ZT <call-offset> <base-encoding>
+ | _ZTc <call-offset> <call-offset> <base-encoding>
+ <call-offset> ::= h <nv-offset>
+ | v <v-offset>
+ <nv-offset> ::= <offset-number> _
+ <v-offset> ::= <offset-number> _ <offset-number> _
+
+ Args:
+ name: A string, the symbol name to resolve.
+
+ Returns:
+ A string, symbol name of the nominal target function.
+ The input symbol name if it is not a thunk symbol.
+ """
+ if name.startswith('_ZTh') or name.startswith('_ZTv'):
+ lhs, rhs = _ConsumeCallOffset(name[len('_ZT'):])
+ if lhs:
+ return '_Z' + rhs
+ if name.startswith('_ZTc'):
+ lhs, rhs = _ConsumeCallOffset(name[len('_ZTc'):])
+ lhs2, rhs = _ConsumeCallOffset(rhs)
+ if lhs and lhs2:
+ return '_Z' + rhs
+ return name
+
+
+def _FilterElfFunctions(lsdump):
+ """Finds exported functions and thunks in lsdump.
+
+ Args:
+ lsdump: An AttrDict object containing the lsdump.
+
+ Yields:
+ The AttrDict objects in lsdump.elf_functions.
+ """
+ functions = {function.linker_set_key for function in lsdump.functions}
+
+ for elf_function in lsdump.elf_functions:
+ if elf_function.name in functions:
+ yield elf_function
+ elif _FindThunkTarget(elf_function.name) in functions:
+ yield elf_function
+
+
+def _FilterElfObjects(lsdump):
+ """Finds exported variables, type info, and vtables in lsdump.
+
+ Args:
+ lsdump: An AttrDict object containing the lsdump.
+
+ Yields:
+ The AttrDict objects in lsdump.elf_objects.
+ """
+ global_vars = {global_var.linker_set_key
+ for global_var in lsdump.global_vars}
+ record_names = {record_type.unique_id[len('_ZTS'):]
+ for record_type in lsdump.record_types}
+
+ for elf_object in lsdump.elf_objects:
+ name = elf_object.name
+ if name in global_vars:
+ yield elf_object
+ elif (name[:len('_ZTS')] in {'_ZTV', '_ZTT', '_ZTI', '_ZTS'} and
+ name[len('_ZTS'):] in record_names):
+ yield elf_object
+
+
+def _ParseSymbolsFromLsdump(lsdump, output_dump):
+ """Parses symbols from an lsdump.
+
+ Args:
+ lsdump: An AttrDict object containing the lsdump.
+ output_dump: An AttrDict object containing the output.
+ """
+ output_dump.elf_functions = list(_FilterElfFunctions(lsdump))
+ output_dump.elf_objects = list(_FilterElfObjects(lsdump))
+
+
+def _ParseVtablesFromLsdump(lsdump, output_dump):
+ """Parses vtables from an lsdump.
+
+ Args:
+ lsdump: An AttrDict object containing the lsdump.
+ output_dump: An AttrDict object containing the output.
+ """
+ vtable_symbols = {elf_object.name for elf_object in lsdump.elf_objects
+ if elf_object.name.startswith('_ZTV')}
+
+ output_dump.record_types = []
+ for lsdump_record_type in lsdump.record_types:
+ type_symbol = lsdump_record_type.unique_id
+ vtable_symbol = '_ZTV' + type_symbol[len('_ZTS'):]
+ if vtable_symbol not in vtable_symbols:
+ continue
+ record_type = AttrDict()
+ record_type.unique_id = lsdump_record_type.unique_id
+ record_type.vtable_components = lsdump_record_type.vtable_components
+ output_dump.record_types.append(record_type)
+
+
+def ParseLsdumpFile(input_path, output_path):
+ """Converts an lsdump file to a dump file for the ABI test.
+
+ Args:
+ input_path: The path to the (gzipped) lsdump file.
+ output_path: The path to the output dump file.
+
+ Raises:
+ LsdumpError if fails to create the dump file.
+ """
+ try:
+ with _OpenFileOrGzipped(input_path) as lsdump_file:
+ lsdump = json.load(lsdump_file, object_hook=AttrDict)
+ except (IOError, ValueError) as e:
+ raise LsdumpError(e)
+
+ try:
+ output_dump = AttrDict()
+ _ParseVtablesFromLsdump(lsdump, output_dump)
+ _ParseSymbolsFromLsdump(lsdump, output_dump)
+ except AttributeError as e:
+ raise LsdumpError(e)
+
+ abs_output_path = os.path.abspath(output_path)
+ abs_output_dir = os.path.dirname(abs_output_path)
+
+ try:
+ if abs_output_dir and not os.path.exists(abs_output_dir):
+ os.makedirs(abs_output_dir)
+ with open(output_path, 'wb') as output_file:
+ json.dump(output_dump, output_file,
+ indent=1, separators=(',', ':'))
+ except (IOError, OSError) as e:
+ raise LsdumpError(e)
+
+
+def main():
+ arg_parser = argparse.ArgumentParser(
+ description='This script converts an lsdump file to a dump file for '
+ 'the ABI test in VTS.')
+ arg_parser.add_argument('input_path',
+ help='input lsdump file path.')
+ arg_parser.add_argument('output_path',
+ help='output dump file path.')
+ args = arg_parser.parse_args()
+
+ try:
+ ParseLsdumpFile(args.input_path, args.output_path)
+ except LsdumpError as e:
+ print(e)
+ exit(1)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/golden/vndk_data.py b/golden/vndk_data.py
index 014453d..e215a91 100644
--- a/golden/vndk_data.py
+++ b/golden/vndk_data.py
@@ -14,32 +14,32 @@
# limitations under the License.
#
-import csv
+import collections
import logging
import os
-# The tags in VNDK spreadsheet:
+# The tags in VNDK list:
# Low-level NDK libraries that can be used by framework and vendor modules.
-LL_NDK = "LL-NDK"
+LL_NDK = "LLNDK"
# LL-NDK dependencies that vendor modules cannot directly access.
-LL_NDK_PRIVATE = "LL-NDK-Private"
+LL_NDK_PRIVATE = "LLNDK-private"
# Same-process HAL implementation in vendor partition.
SP_HAL = "SP-HAL"
# Framework libraries that can be used by vendor modules except same-process HAL
# and its dependencies in vendor partition.
-VNDK = "VNDK"
+VNDK = "VNDK-core"
# VNDK dependencies that vendor modules cannot directly access.
-VNDK_PRIVATE = "VNDK-Private"
+VNDK_PRIVATE = "VNDK-core-private"
# Same-process HAL dependencies in framework.
VNDK_SP = "VNDK-SP"
# VNDK-SP dependencies that vendor modules cannot directly access.
-VNDK_SP_PRIVATE = "VNDK-SP-Private"
+VNDK_SP_PRIVATE = "VNDK-SP-private"
# The ABI dump directories. 64-bit comes before 32-bit in order to sequentially
# search for longest prefix.
@@ -48,6 +48,8 @@
# The data directory.
_GOLDEN_DIR = os.path.join("vts", "testcases", "vndk", "golden")
+# Regular expression prefix for library name patterns.
+_REGEX_PREFIX = "[regex]"
def LoadDefaultVndkVersion(data_file_path):
"""Loads the name of the data directory for devices with no VNDK version.
@@ -106,6 +108,52 @@
return dump_dir
+def _LoadVndkLibraryListsFile(vndk_lists, tags, vndk_lib_list_path):
+ """Load VNDK libraries from the file to the specified tuple.
+
+ Args:
+ vndk_lists: The output tuple of lists containing library names.
+ tags: Strings, the tags of the libraries to find.
+ vndk_lib_list_path: The path to load the VNDK library list.
+ """
+
+ lib_sets = collections.defaultdict(set)
+
+ # Load VNDK tags from the list.
+ with open(vndk_lib_list_path) as vndk_lib_list_file:
+ for line in vndk_lib_list_file:
+ # Ignore comments.
+ if line.startswith('#'):
+ continue
+
+ # Split columns.
+ cells = line.split(': ', 1)
+ if len(cells) < 2:
+ continue
+ tag = cells[0]
+ lib_name = cells[1].strip()
+
+ lib_sets[tag].add(lib_name)
+
+ # Compute VNDK-core-private and VNDK-SP-private.
+ private = lib_sets.get('VNDK-private', set())
+
+ lib_sets[LL_NDK_PRIVATE].update(lib_sets[LL_NDK] & private)
+ lib_sets[VNDK_PRIVATE].update(lib_sets[VNDK] & private)
+ lib_sets[VNDK_SP_PRIVATE].update(lib_sets[VNDK_SP] & private)
+
+ lib_sets[LL_NDK].difference_update(private)
+ lib_sets[VNDK].difference_update(private)
+ lib_sets[VNDK_SP].difference_update(private)
+
+ # Update the output entries.
+ for index, tag in enumerate(tags):
+ for lib_name in lib_sets.get(tag, tuple()):
+ if lib_name.startswith(_REGEX_PREFIX):
+ lib_name = lib_name[len(_REGEX_PREFIX):]
+ vndk_lists[index].append(lib_name)
+
+
def LoadVndkLibraryLists(data_file_path, version, *tags):
"""Find the VNDK libraries with specific tags.
@@ -125,25 +173,20 @@
if not version_dir:
return None
- path = os.path.join(
- data_file_path, _GOLDEN_DIR, version_dir, "eligible-list.csv")
- if not os.path.isfile(path):
- logging.warning("Cannot load %s.", path)
+ vndk_lib_list_path = os.path.join(
+ data_file_path, _GOLDEN_DIR, version_dir, "vndk-lib-list.txt")
+ if not os.path.isfile(vndk_lib_list_path):
+ logging.warning("Cannot load %s.", vndk_lib_list_path)
return None
- dir_suffix = "-" + version if version else ""
+ vndk_lib_extra_list_path = os.path.join(
+ data_file_path, _GOLDEN_DIR, version_dir, "vndk-lib-extra-list.txt")
+ if not os.path.isfile(vndk_lib_extra_list_path):
+ logging.warning("Cannot load %s.", vndk_lib_extra_list_path)
+ return None
+
vndk_lists = tuple([] for x in tags)
- with open(path) as csv_file:
- # Skip header
- next(csv_file)
- reader = csv.reader(csv_file)
- for cells in reader:
- for tag_index, tag in enumerate(tags):
- if tag == cells[1]:
- lib_name = cells[0].replace("${VNDK_VER}", dir_suffix)
- if lib_name.startswith("[regex]"):
- lib_name = lib_name[len("[regex]"):]
- vndk_lists[tag_index].extend(
- lib_name.replace("${LIB}", lib)
- for lib in ("lib", "lib64"))
+
+ _LoadVndkLibraryListsFile(vndk_lists, tags, vndk_lib_list_path)
+ _LoadVndkLibraryListsFile(vndk_lists, tags, vndk_lib_extra_list_path)
return vndk_lists
diff --git a/open_libraries/VtsVndkOpenLibrariesTest.py b/open_libraries/VtsVndkOpenLibrariesTest.py
index f51502d..8dbfda6 100644
--- a/open_libraries/VtsVndkOpenLibrariesTest.py
+++ b/open_libraries/VtsVndkOpenLibrariesTest.py
@@ -23,6 +23,7 @@
from vts.runners.host import keys
from vts.runners.host import test_runner
from vts.testcases.vndk.golden import vndk_data
+from vts.utils.python.os import path_utils
from vts.utils.python.vndk import vndk_utils
@@ -110,19 +111,20 @@
vndk_data.VNDK_SP,
vndk_data.VNDK_SP_PRIVATE)
asserts.assertTrue(vndk_lists, "Cannot load VNDK library lists.")
- allowed_libs = set()
- for vndk_list in vndk_lists:
- allowed_libs.update(vndk_list)
+ allowed_libs = set().union(*vndk_lists)
logging.debug("Allowed system libraries: %s", allowed_libs)
asserts.assertTrue(self._dut.isAdbRoot,
"Must be root to find all libraries in use.")
cmds = self._ListProcessCommands(lambda x: (x.startswith("/odm/") or
x.startswith("/vendor/")))
- deps = self._ListOpenFiles(cmds.keys(),
- lambda x: (x.startswith("/system/") and
- x.endswith(".so") and
- x not in allowed_libs))
+
+ def _IsDisallowedSystemLib(lib_path):
+ return (lib_path.startswith("/system/") and
+ lib_path.endswith(".so") and
+ path_utils.TargetBaseName(lib_path) not in allowed_libs)
+
+ deps = self._ListOpenFiles(cmds.keys(), _IsDisallowedSystemLib)
if deps:
error_lines = ["%s %s %s" % (pid, cmds[pid], libs)
for pid, libs in deps.iteritems()]